agent-embassy 1.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 (145) hide show
  1. package/CHANGELOG.md +61 -0
  2. package/CONTRIBUTING.md +122 -0
  3. package/LICENSE +21 -0
  4. package/README.md +185 -0
  5. package/README.zh-CN.md +185 -0
  6. package/SECURITY.md +224 -0
  7. package/assets/live-dashboard/app.css +1610 -0
  8. package/assets/mark-seal.svg +7 -0
  9. package/assets/mark.svg +7 -0
  10. package/assets/social-preview.png +0 -0
  11. package/assets/vendor/react/LICENSE +21 -0
  12. package/assets/vendor/react/react-dom.production.min.js +267 -0
  13. package/assets/vendor/react/react.production.min.js +31 -0
  14. package/dist/src/errors.d.ts +5 -0
  15. package/dist/src/errors.js +11 -0
  16. package/dist/src/errors.js.map +1 -0
  17. package/dist/src/gateway/claude-compatibility-scratch.d.ts +18 -0
  18. package/dist/src/gateway/claude-compatibility-scratch.js +158 -0
  19. package/dist/src/gateway/claude-compatibility-scratch.js.map +1 -0
  20. package/dist/src/gateway/claude-helper-client.d.ts +43 -0
  21. package/dist/src/gateway/claude-helper-client.js +212 -0
  22. package/dist/src/gateway/claude-helper-client.js.map +1 -0
  23. package/dist/src/gateway/claude-helper-protocol.d.ts +172 -0
  24. package/dist/src/gateway/claude-helper-protocol.js +373 -0
  25. package/dist/src/gateway/claude-helper-protocol.js.map +1 -0
  26. package/dist/src/gateway/claude-helper-supervisor.d.ts +74 -0
  27. package/dist/src/gateway/claude-helper-supervisor.js +497 -0
  28. package/dist/src/gateway/claude-helper-supervisor.js.map +1 -0
  29. package/dist/src/gateway/claude-helper.d.ts +1 -0
  30. package/dist/src/gateway/claude-helper.js +269 -0
  31. package/dist/src/gateway/claude-helper.js.map +1 -0
  32. package/dist/src/gateway/claude-peer.d.ts +273 -0
  33. package/dist/src/gateway/claude-peer.js +2359 -0
  34. package/dist/src/gateway/claude-peer.js.map +1 -0
  35. package/dist/src/gateway/claude-runtime.d.ts +40 -0
  36. package/dist/src/gateway/claude-runtime.js +302 -0
  37. package/dist/src/gateway/claude-runtime.js.map +1 -0
  38. package/dist/src/gateway/cli-copy.d.ts +8 -0
  39. package/dist/src/gateway/cli-copy.en.d.ts +13 -0
  40. package/dist/src/gateway/cli-copy.en.js +43 -0
  41. package/dist/src/gateway/cli-copy.en.js.map +1 -0
  42. package/dist/src/gateway/cli-copy.js +19 -0
  43. package/dist/src/gateway/cli-copy.js.map +1 -0
  44. package/dist/src/gateway/cli-copy.zh-CN.d.ts +13 -0
  45. package/dist/src/gateway/cli-copy.zh-CN.js +43 -0
  46. package/dist/src/gateway/cli-copy.zh-CN.js.map +1 -0
  47. package/dist/src/gateway/cli.d.ts +44 -0
  48. package/dist/src/gateway/cli.js +969 -0
  49. package/dist/src/gateway/cli.js.map +1 -0
  50. package/dist/src/gateway/codex-app-server.d.ts +310 -0
  51. package/dist/src/gateway/codex-app-server.js +1746 -0
  52. package/dist/src/gateway/codex-app-server.js.map +1 -0
  53. package/dist/src/gateway/codex-local-transport.d.ts +81 -0
  54. package/dist/src/gateway/codex-local-transport.js +617 -0
  55. package/dist/src/gateway/codex-local-transport.js.map +1 -0
  56. package/dist/src/gateway/codex-registration-generation.d.ts +5 -0
  57. package/dist/src/gateway/codex-registration-generation.js +19 -0
  58. package/dist/src/gateway/codex-registration-generation.js.map +1 -0
  59. package/dist/src/gateway/codex-registration-succession.d.ts +209 -0
  60. package/dist/src/gateway/codex-registration-succession.js +588 -0
  61. package/dist/src/gateway/codex-registration-succession.js.map +1 -0
  62. package/dist/src/gateway/compatibility.d.ts +70 -0
  63. package/dist/src/gateway/compatibility.js +252 -0
  64. package/dist/src/gateway/compatibility.js.map +1 -0
  65. package/dist/src/gateway/config.d.ts +24 -0
  66. package/dist/src/gateway/config.js +130 -0
  67. package/dist/src/gateway/config.js.map +1 -0
  68. package/dist/src/gateway/control.d.ts +308 -0
  69. package/dist/src/gateway/control.js +1573 -0
  70. package/dist/src/gateway/control.js.map +1 -0
  71. package/dist/src/gateway/dashboard-copy.d.ts +7 -0
  72. package/dist/src/gateway/dashboard-copy.en.d.ts +506 -0
  73. package/dist/src/gateway/dashboard-copy.en.js +507 -0
  74. package/dist/src/gateway/dashboard-copy.en.js.map +1 -0
  75. package/dist/src/gateway/dashboard-copy.js +516 -0
  76. package/dist/src/gateway/dashboard-copy.js.map +1 -0
  77. package/dist/src/gateway/dashboard-copy.zh-CN.d.ts +506 -0
  78. package/dist/src/gateway/dashboard-copy.zh-CN.js +507 -0
  79. package/dist/src/gateway/dashboard-copy.zh-CN.js.map +1 -0
  80. package/dist/src/gateway/dashboard-model.d.ts +207 -0
  81. package/dist/src/gateway/dashboard-model.js +720 -0
  82. package/dist/src/gateway/dashboard-model.js.map +1 -0
  83. package/dist/src/gateway/dashboard.d.ts +26 -0
  84. package/dist/src/gateway/dashboard.js +941 -0
  85. package/dist/src/gateway/dashboard.js.map +1 -0
  86. package/dist/src/gateway/delivery-machine.d.ts +235 -0
  87. package/dist/src/gateway/delivery-machine.js +540 -0
  88. package/dist/src/gateway/delivery-machine.js.map +1 -0
  89. package/dist/src/gateway/instance-lease.d.ts +21 -0
  90. package/dist/src/gateway/instance-lease.js +549 -0
  91. package/dist/src/gateway/instance-lease.js.map +1 -0
  92. package/dist/src/gateway/live-dashboard-app/app.js +2914 -0
  93. package/dist/src/gateway/live-dashboard-assets.d.ts +10 -0
  94. package/dist/src/gateway/live-dashboard-assets.js +73 -0
  95. package/dist/src/gateway/live-dashboard-assets.js.map +1 -0
  96. package/dist/src/gateway/live-dashboard-bootstrap.d.ts +36 -0
  97. package/dist/src/gateway/live-dashboard-bootstrap.js +176 -0
  98. package/dist/src/gateway/live-dashboard-bootstrap.js.map +1 -0
  99. package/dist/src/gateway/live-dashboard-command.d.ts +54 -0
  100. package/dist/src/gateway/live-dashboard-command.js +324 -0
  101. package/dist/src/gateway/live-dashboard-command.js.map +1 -0
  102. package/dist/src/gateway/live-dashboard-http.d.ts +41 -0
  103. package/dist/src/gateway/live-dashboard-http.js +445 -0
  104. package/dist/src/gateway/live-dashboard-http.js.map +1 -0
  105. package/dist/src/gateway/live-dashboard-protocol.d.ts +38 -0
  106. package/dist/src/gateway/live-dashboard-protocol.js +156 -0
  107. package/dist/src/gateway/live-dashboard-protocol.js.map +1 -0
  108. package/dist/src/gateway/live-dashboard-server.d.ts +33 -0
  109. package/dist/src/gateway/live-dashboard-server.js +130 -0
  110. package/dist/src/gateway/live-dashboard-server.js.map +1 -0
  111. package/dist/src/gateway/live-dashboard-stream.d.ts +46 -0
  112. package/dist/src/gateway/live-dashboard-stream.js +233 -0
  113. package/dist/src/gateway/live-dashboard-stream.js.map +1 -0
  114. package/dist/src/gateway/live-dashboard.d.ts +31 -0
  115. package/dist/src/gateway/live-dashboard.js +190 -0
  116. package/dist/src/gateway/live-dashboard.js.map +1 -0
  117. package/dist/src/gateway/locale.d.ts +4 -0
  118. package/dist/src/gateway/locale.js +10 -0
  119. package/dist/src/gateway/locale.js.map +1 -0
  120. package/dist/src/gateway/progress-watch-machine.d.ts +97 -0
  121. package/dist/src/gateway/progress-watch-machine.js +172 -0
  122. package/dist/src/gateway/progress-watch-machine.js.map +1 -0
  123. package/dist/src/gateway/providers.d.ts +287 -0
  124. package/dist/src/gateway/providers.js +2451 -0
  125. package/dist/src/gateway/providers.js.map +1 -0
  126. package/dist/src/gateway/server.d.ts +57 -0
  127. package/dist/src/gateway/server.js +264 -0
  128. package/dist/src/gateway/server.js.map +1 -0
  129. package/dist/src/gateway/service.d.ts +449 -0
  130. package/dist/src/gateway/service.js +4831 -0
  131. package/dist/src/gateway/service.js.map +1 -0
  132. package/dist/src/gateway/store.d.ts +340 -0
  133. package/dist/src/gateway/store.js +4047 -0
  134. package/dist/src/gateway/store.js.map +1 -0
  135. package/dist/src/gateway/types.d.ts +514 -0
  136. package/dist/src/gateway/types.js +414 -0
  137. package/dist/src/gateway/types.js.map +1 -0
  138. package/dist/src/mutex.d.ts +4 -0
  139. package/dist/src/mutex.js +23 -0
  140. package/dist/src/mutex.js.map +1 -0
  141. package/docs/DESIGN.md +134 -0
  142. package/docs/GATEWAY-ARCHITECTURE.md +858 -0
  143. package/package.json +80 -0
  144. package/skills/embassy-peer/SKILL.md +198 -0
  145. package/skills/embassy-peer/agents/openai.yaml +4 -0
@@ -0,0 +1,2914 @@
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 tableLookup(table, state) {
95
+ return Object.prototype.hasOwnProperty.call(table, state)
96
+ ? table[state]
97
+ : "unknown";
98
+ }
99
+ const DELIVERY_CHIP_KINDS = {
100
+ queued: "progress",
101
+ dispatching: "progress",
102
+ transport_written: "progress",
103
+ held: "progress",
104
+ duplicate: "inert",
105
+ delivered: "positive",
106
+ unconfirmed: "indeterminate",
107
+ ambiguous: "indeterminate",
108
+ failed: "failure",
109
+ expired: "failure",
110
+ rejected: "warning",
111
+ cancelled: "inert",
112
+ abandoned: "inert",
113
+ };
114
+ const ROUTE_CHIP_KINDS = {
115
+ idle: "progress",
116
+ busy: "active",
117
+ awaiting_approval: "warning",
118
+ stale: "failure",
119
+ offline: "failure",
120
+ incompatible: "failure",
121
+ disabled: "inert",
122
+ };
123
+ const PEER_CHIP_KINDS = {
124
+ idle: "progress",
125
+ busy: "active",
126
+ awaiting_approval: "warning",
127
+ offline: "inert",
128
+ incompatible: "failure",
129
+ };
130
+ const HEALTH_CHIP_KINDS = {
131
+ healthy: "positive",
132
+ connecting: "progress",
133
+ degraded: "warning",
134
+ offline: "failure",
135
+ incompatible: "failure",
136
+ };
137
+ const COMPATIBILITY_CHIP_KINDS = {
138
+ compatible: "positive",
139
+ unknown: "inert",
140
+ expired: "warning",
141
+ incompatible: "failure",
142
+ };
143
+ const OVERALL_CHIP_KINDS = {
144
+ ready: "positive",
145
+ setup: "progress",
146
+ attention: "failure",
147
+ };
148
+ const PARTY_CHIP_KINDS = {
149
+ ready: "positive",
150
+ busy: "active",
151
+ waiting: "warning",
152
+ missing: "inert",
153
+ attention: "failure",
154
+ };
155
+ const SEVERITY_CHIP_KINDS = {
156
+ info: "active",
157
+ warning: "warning",
158
+ error: "failure",
159
+ };
160
+ const CONNECTION_CHIP_KINDS = {
161
+ connected: "positive",
162
+ connecting: "progress",
163
+ paused: "inert",
164
+ unavailable: "warning",
165
+ disconnected: "warning",
166
+ stopped: "warning",
167
+ };
168
+ function chipKindFor(state, direction, safeErrorCode) {
169
+ if (state === "rejected" && safeErrorCode === "SENDER_NOT_PAIRED") {
170
+ return "inert";
171
+ }
172
+ if (state === "delivered" && direction === "codex_to_claude") {
173
+ return "qualified";
174
+ }
175
+ return tableLookup(DELIVERY_CHIP_KINDS, state);
176
+ }
177
+ Embassy.chipKindFor = chipKindFor;
178
+ function routeChipKind(state) {
179
+ return tableLookup(ROUTE_CHIP_KINDS, state);
180
+ }
181
+ Embassy.routeChipKind = routeChipKind;
182
+ function peerChipKind(state) {
183
+ return tableLookup(PEER_CHIP_KINDS, state);
184
+ }
185
+ Embassy.peerChipKind = peerChipKind;
186
+ function healthChipKind(state) {
187
+ return tableLookup(HEALTH_CHIP_KINDS, state);
188
+ }
189
+ Embassy.healthChipKind = healthChipKind;
190
+ function compatibilityChipKind(state) {
191
+ return tableLookup(COMPATIBILITY_CHIP_KINDS, state);
192
+ }
193
+ Embassy.compatibilityChipKind = compatibilityChipKind;
194
+ function overallChipKind(state) {
195
+ return tableLookup(OVERALL_CHIP_KINDS, state);
196
+ }
197
+ Embassy.overallChipKind = overallChipKind;
198
+ function partyChipKind(state) {
199
+ return tableLookup(PARTY_CHIP_KINDS, state);
200
+ }
201
+ Embassy.partyChipKind = partyChipKind;
202
+ function severityChipKind(state) {
203
+ return tableLookup(SEVERITY_CHIP_KINDS, state);
204
+ }
205
+ Embassy.severityChipKind = severityChipKind;
206
+ function connectionChipKind(state) {
207
+ return tableLookup(CONNECTION_CHIP_KINDS, state);
208
+ }
209
+ Embassy.connectionChipKind = connectionChipKind;
210
+ function chipKindByDomain(domain, state, direction) {
211
+ switch (domain) {
212
+ case "delivery":
213
+ return chipKindFor(state, direction);
214
+ case "route":
215
+ return routeChipKind(state);
216
+ case "peer":
217
+ return peerChipKind(state);
218
+ case "health":
219
+ return healthChipKind(state);
220
+ case "compatibility":
221
+ return compatibilityChipKind(state);
222
+ case "overall":
223
+ return overallChipKind(state);
224
+ case "party":
225
+ return partyChipKind(state);
226
+ case "severity":
227
+ return severityChipKind(state);
228
+ case "connection":
229
+ return connectionChipKind(state);
230
+ }
231
+ }
232
+ Embassy.chipKindByDomain = chipKindByDomain;
233
+ function camelCaseToken(token) {
234
+ return token.replace(/_([a-z])/g, (_match, character) => character.toUpperCase());
235
+ }
236
+ Embassy.camelCaseToken = camelCaseToken;
237
+ function deliveryMeaningKey(state, safeErrorCode, direction) {
238
+ switch (state) {
239
+ case "queued":
240
+ return "activity.meaning.queued";
241
+ case "dispatching":
242
+ return "activity.meaning.dispatching";
243
+ case "transport_written":
244
+ return "activity.meaning.transportWritten";
245
+ case "held":
246
+ return "activity.meaning.held";
247
+ case "duplicate":
248
+ return "activity.meaning.duplicate";
249
+ case "delivered":
250
+ return direction === "codex_to_claude"
251
+ ? "activity.meaning.delivered.codexToClaude"
252
+ : direction === "claude_to_codex"
253
+ ? "activity.meaning.delivered.claudeToCodex"
254
+ : "activity.meaning.delivered";
255
+ case "unconfirmed":
256
+ return "activity.meaning.unconfirmed";
257
+ case "ambiguous":
258
+ return "activity.meaning.ambiguous";
259
+ case "failed":
260
+ return "activity.meaning.failed";
261
+ case "expired":
262
+ return "activity.meaning.expired";
263
+ case "rejected":
264
+ return safeErrorCode === "SENDER_NOT_PAIRED"
265
+ ? "activity.meaning.senderNotPaired"
266
+ : "activity.meaning.rejected";
267
+ case "cancelled":
268
+ return "activity.meaning.cancelled";
269
+ case "abandoned":
270
+ switch (safeErrorCode) {
271
+ case "CONTROLLER_RESTARTED":
272
+ return "activity.meaning.abandoned.controllerRestarted";
273
+ case "TRANSIENT_BODY_UNAVAILABLE":
274
+ return "activity.meaning.abandoned.transientBody";
275
+ case "ROUTE_UNREGISTERED":
276
+ case "MESSAGE_EXPIRED":
277
+ return "activity.meaning.abandoned.routeTerminated";
278
+ default:
279
+ return "activity.meaning.abandoned.generic";
280
+ }
281
+ default:
282
+ return "activity.meaning.other";
283
+ }
284
+ }
285
+ Embassy.deliveryMeaningKey = deliveryMeaningKey;
286
+ function meaningKeyFor(domain, state, safeErrorCode, direction) {
287
+ switch (domain) {
288
+ case "delivery":
289
+ return deliveryMeaningKey(state, safeErrorCode, direction);
290
+ case "route":
291
+ return `route.meaning.${camelCaseToken(state)}`;
292
+ case "peer":
293
+ return `peer.meaning.${camelCaseToken(state)}`;
294
+ case "health":
295
+ return `health.meaning.${camelCaseToken(state)}`;
296
+ case "compatibility":
297
+ return `compatibility.meaning.${camelCaseToken(state)}`;
298
+ case "overall":
299
+ return `overall.${camelCaseToken(state)}`;
300
+ case "party":
301
+ return `status.${camelCaseToken(state)}`;
302
+ case "severity":
303
+ return `severity.${camelCaseToken(state)}`;
304
+ case "connection":
305
+ return `live.connection.${state}`;
306
+ }
307
+ }
308
+ Embassy.meaningKeyFor = meaningKeyFor;
309
+ })(Embassy || (Embassy = {}));
310
+ var Embassy;
311
+ (function (Embassy) {
312
+ const PLACEHOLDER_PATTERN = /\{([A-Za-z][A-Za-z0-9]*)\}/g;
313
+ function interpolate(template, values = {}) {
314
+ return template.replace(PLACEHOLDER_PATTERN, (match, name) => Object.prototype.hasOwnProperty.call(values, name)
315
+ ? String(values[name])
316
+ : match);
317
+ }
318
+ Embassy.interpolate = interpolate;
319
+ const I18nContext = React.createContext(null);
320
+ function I18nProvider(props) {
321
+ const { copy, onLocaleChange } = props;
322
+ const [locale, setLocaleState] = React.useState(props.initialLocale);
323
+ const setLocale = React.useCallback((next) => {
324
+ setLocaleState(next);
325
+ if (onLocaleChange !== undefined)
326
+ onLocaleChange(next);
327
+ }, [onLocaleChange]);
328
+ const t = React.useCallback((key, values) => {
329
+ const template = copy[locale][key] ?? key;
330
+ return interpolate(template, values ?? {});
331
+ }, [copy, locale]);
332
+ const value = React.useMemo(() => ({ locale, setLocale, t }), [locale, setLocale, t]);
333
+ return (React.createElement(I18nContext.Provider, { value: value }, props.children));
334
+ }
335
+ Embassy.I18nProvider = I18nProvider;
336
+ function useI18n() {
337
+ const value = React.useContext(I18nContext);
338
+ if (value === null) {
339
+ throw new Error("Embassy.I18nProvider is missing above this component.");
340
+ }
341
+ return value;
342
+ }
343
+ function useT() {
344
+ return useI18n().t;
345
+ }
346
+ Embassy.useT = useT;
347
+ function useLocale() {
348
+ const { locale, setLocale } = useI18n();
349
+ return [locale, setLocale];
350
+ }
351
+ Embassy.useLocale = useLocale;
352
+ })(Embassy || (Embassy = {}));
353
+ var Embassy;
354
+ (function (Embassy) {
355
+ Embassy.TERMINAL_DELIVERY_STATES = [
356
+ "delivered",
357
+ "unconfirmed",
358
+ "failed",
359
+ "ambiguous",
360
+ "expired",
361
+ "cancelled",
362
+ "abandoned",
363
+ "rejected",
364
+ ];
365
+ const TERMINAL_DELIVERY_STATE_SET = new Set(Embassy.TERMINAL_DELIVERY_STATES);
366
+ Embassy.PULSE_WINDOW_MS = 3_600_000;
367
+ const HEALTH_WORST_FIRST = [
368
+ "offline",
369
+ "incompatible",
370
+ "degraded",
371
+ "connecting",
372
+ "healthy",
373
+ ];
374
+ const COMPATIBILITY_WORST_FIRST = [
375
+ "incompatible",
376
+ "expired",
377
+ "unknown",
378
+ "compatible",
379
+ ];
380
+ function compareText(left, right) {
381
+ return left < right ? -1 : left > right ? 1 : 0;
382
+ }
383
+ function isTerminalDeliveryState(state) {
384
+ return TERMINAL_DELIVERY_STATE_SET.has(state);
385
+ }
386
+ Embassy.isTerminalDeliveryState = isTerminalDeliveryState;
387
+ function parseTimestampMs(iso) {
388
+ if (iso === undefined)
389
+ return undefined;
390
+ const milliseconds = Date.parse(iso);
391
+ return Number.isFinite(milliseconds) ? milliseconds : undefined;
392
+ }
393
+ Embassy.parseTimestampMs = parseTimestampMs;
394
+ function queueSplit(model, targetProvider, nowMs) {
395
+ let depth = 0;
396
+ let oldestQueuedAt;
397
+ let oldestQueuedMs;
398
+ for (const route of model.routes) {
399
+ if (route.provider !== targetProvider)
400
+ continue;
401
+ depth += route.queueDepth;
402
+ if (route.queueDepth === 0 || route.oldestQueuedAt === undefined) {
403
+ continue;
404
+ }
405
+ const queuedMs = parseTimestampMs(route.oldestQueuedAt);
406
+ if (queuedMs === undefined)
407
+ continue;
408
+ if (oldestQueuedMs === undefined || queuedMs < oldestQueuedMs) {
409
+ oldestQueuedMs = queuedMs;
410
+ oldestQueuedAt = route.oldestQueuedAt;
411
+ }
412
+ }
413
+ return {
414
+ depth,
415
+ depthIsLowerBound: model.omissions.routes > 0,
416
+ oldestQueuedAt,
417
+ oldestAgeMs: oldestQueuedMs === undefined
418
+ ? undefined
419
+ : Math.max(0, nowMs - oldestQueuedMs),
420
+ };
421
+ }
422
+ Embassy.queueSplit = queueSplit;
423
+ function routeOldestAgeMs(route, nowMs) {
424
+ if (route.queueDepth === 0 || route.oldestQueuedAt === undefined) {
425
+ return undefined;
426
+ }
427
+ const queuedMs = parseTimestampMs(route.oldestQueuedAt);
428
+ return queuedMs === undefined ? undefined : Math.max(0, nowMs - queuedMs);
429
+ }
430
+ Embassy.routeOldestAgeMs = routeOldestAgeMs;
431
+ function worstConnectorHealth(model, provider) {
432
+ let worst;
433
+ for (const connector of model.connectors) {
434
+ if (connector.provider !== provider)
435
+ continue;
436
+ if (worst === undefined ||
437
+ HEALTH_WORST_FIRST.indexOf(connector.health) <
438
+ HEALTH_WORST_FIRST.indexOf(worst)) {
439
+ worst = connector.health;
440
+ }
441
+ }
442
+ return worst;
443
+ }
444
+ Embassy.worstConnectorHealth = worstConnectorHealth;
445
+ function worstCompatibility(model) {
446
+ let worst;
447
+ for (const connector of model.connectors) {
448
+ if (worst === undefined ||
449
+ COMPATIBILITY_WORST_FIRST.indexOf(connector.compatibility) <
450
+ COMPATIBILITY_WORST_FIRST.indexOf(worst)) {
451
+ worst = connector.compatibility;
452
+ }
453
+ }
454
+ return worst;
455
+ }
456
+ Embassy.worstCompatibility = worstCompatibility;
457
+ function pulse(model) {
458
+ const generatedMs = parseTimestampMs(model.generatedAt);
459
+ const cutoffMs = generatedMs === undefined ? undefined : generatedMs - Embassy.PULSE_WINDOW_MS;
460
+ const counts = new Map();
461
+ for (const group of model.activity) {
462
+ if (!isTerminalDeliveryState(group.state))
463
+ continue;
464
+ if (cutoffMs !== undefined) {
465
+ const timestampMs = parseTimestampMs(group.timestamp);
466
+ if (timestampMs === undefined || timestampMs < cutoffMs)
467
+ continue;
468
+ }
469
+ counts.set(group.state, (counts.get(group.state) ?? 0) + 1);
470
+ }
471
+ const bars = Embassy.TERMINAL_DELIVERY_STATES.map((state) => ({ state, count: counts.get(state) ?? 0 }));
472
+ return {
473
+ bars,
474
+ total: bars.reduce((sum, bar) => sum + bar.count, 0),
475
+ isLowerBound: model.omissions.messageGroups > 0 ||
476
+ model.omissions.upstreamMessageEvents > 0,
477
+ };
478
+ }
479
+ Embassy.pulse = pulse;
480
+ function isMonitorOnly(route) {
481
+ return route.safeErrorCode === "CODEX_WRITES_DISABLED";
482
+ }
483
+ Embassy.isMonitorOnly = isMonitorOnly;
484
+ function hasLifecycleTruncation(group) {
485
+ for (let index = 1; index < group.events.length; index += 1) {
486
+ const previous = group.events[index - 1];
487
+ const current = group.events[index];
488
+ if (previous !== undefined &&
489
+ current !== undefined &&
490
+ current.sequence > previous.sequence + 1) {
491
+ return true;
492
+ }
493
+ }
494
+ return false;
495
+ }
496
+ Embassy.hasLifecycleTruncation = hasLifecycleTruncation;
497
+ function deliveriesTruncated(model) {
498
+ return (model.omissions.messageEvents > 0 ||
499
+ model.activity.some(hasLifecycleTruncation));
500
+ }
501
+ Embassy.deliveriesTruncated = deliveriesTruncated;
502
+ function deliveryGroupKey(group) {
503
+ const suffix = group.messageIdSuffix ?? `seq${group.events[0]?.sequence ?? 0}`;
504
+ return `${group.direction}|${group.sourceAlias}|${group.targetAlias}|${suffix}`;
505
+ }
506
+ Embassy.deliveryGroupKey = deliveryGroupKey;
507
+ const GUIDANCE_COPY_KEYS = {
508
+ reobserve_claude: "reobserveClaude",
509
+ reobserve_codex: "reobserveCodex",
510
+ claude_not_observed: "claudeNotObserved",
511
+ codex_stale: "codexStale",
512
+ connector_offline: "connectorOffline",
513
+ route_stale: "routeStale",
514
+ queue_stalled: "queueStalled",
515
+ unconfirmed: "unconfirmed",
516
+ degraded: "degraded",
517
+ codex_succession_busy: "codexSuccessionBusy",
518
+ codex_succession_recovery: "codexSuccessionRecovery",
519
+ progress_watch: "progressWatch",
520
+ generic: "generic",
521
+ };
522
+ function guidanceCopyKey(guidance) {
523
+ return GUIDANCE_COPY_KEYS[guidance];
524
+ }
525
+ Embassy.guidanceCopyKey = guidanceCopyKey;
526
+ function attentionCommand(item) {
527
+ switch (item.guidance) {
528
+ case "reobserve_claude":
529
+ case "claude_not_observed":
530
+ return `embassy select-claude --alias ${item.alias ?? "<alias>"}`;
531
+ case "reobserve_codex":
532
+ case "codex_stale":
533
+ return `embassy register-codex --alias ${item.alias ?? "<alias>"}`;
534
+ case "codex_succession_busy":
535
+ case "codex_succession_recovery":
536
+ return `embassy register-codex --alias <new> --succeeds ${item.alias ?? "<old>"}`;
537
+ default:
538
+ return "embassy status";
539
+ }
540
+ }
541
+ Embassy.attentionCommand = attentionCommand;
542
+ function attentionViews(model) {
543
+ return model.attention.map((item) => ({
544
+ item,
545
+ guidanceKey: guidanceCopyKey(item.guidance),
546
+ command: attentionCommand(item),
547
+ }));
548
+ }
549
+ Embassy.attentionViews = attentionViews;
550
+ function extractSuccessions(model) {
551
+ const successions = [];
552
+ for (const item of model.attention) {
553
+ if (item.guidance === "codex_succession_busy") {
554
+ successions.push({
555
+ item,
556
+ guidanceKey: "codexSuccessionBusy",
557
+ command: attentionCommand(item),
558
+ });
559
+ }
560
+ else if (item.guidance === "codex_succession_recovery") {
561
+ successions.push({
562
+ item,
563
+ guidanceKey: "codexSuccessionRecovery",
564
+ command: attentionCommand(item),
565
+ });
566
+ }
567
+ }
568
+ return successions;
569
+ }
570
+ Embassy.extractSuccessions = extractSuccessions;
571
+ function overviewProps(model, nowMs) {
572
+ return {
573
+ generatedAt: model.generatedAt,
574
+ inboundMode: model.inboundMode,
575
+ overall: model.overall,
576
+ statusStrip: {
577
+ broker: model.health,
578
+ claudeConnector: worstConnectorHealth(model, "claude"),
579
+ codexConnector: worstConnectorHealth(model, "codex"),
580
+ compatibility: worstCompatibility(model),
581
+ },
582
+ exchange: {
583
+ ...model.exchange,
584
+ codex: {
585
+ ...model.exchange.codex,
586
+ monitorOnly: model.routes.filter((route) => route.provider === "codex" && isMonitorOnly(route)).length,
587
+ },
588
+ },
589
+ queueClaudeToCodex: queueSplit(model, "codex", nowMs),
590
+ queueCodexToClaude: queueSplit(model, "claude", nowMs),
591
+ graph: model.graph,
592
+ attention: attentionViews(model),
593
+ attentionOmitted: model.omissions.attentionItems,
594
+ pulse: pulse(model),
595
+ };
596
+ }
597
+ Embassy.overviewProps = overviewProps;
598
+ function deliveriesGroups(model) {
599
+ return model.activity.map((group) => ({
600
+ key: deliveryGroupKey(group),
601
+ group,
602
+ routePair: `${group.sourceAlias} → ${group.targetAlias}`,
603
+ eventsTruncated: hasLifecycleTruncation(group),
604
+ }));
605
+ }
606
+ Embassy.deliveriesGroups = deliveriesGroups;
607
+ function routesProps(model, nowMs) {
608
+ const codexRoutes = model.routes
609
+ .filter((route) => route.provider === "codex")
610
+ .map((route) => ({
611
+ route,
612
+ monitorOnly: isMonitorOnly(route),
613
+ oldestAgeMs: routeOldestAgeMs(route, nowMs),
614
+ }));
615
+ return {
616
+ inboundMode: model.inboundMode,
617
+ peers: model.peers,
618
+ peersOmitted: model.omissions.availablePeers,
619
+ codexRoutes,
620
+ routesOmitted: model.omissions.routes,
621
+ pairs: model.pairs,
622
+ pairsOmitted: model.omissions.pairs,
623
+ graph: model.graph,
624
+ successions: extractSuccessions(model),
625
+ };
626
+ }
627
+ Embassy.routesProps = routesProps;
628
+ function activityRowSource(row) {
629
+ if (row.kind === "delivery")
630
+ return row.group.sourceAlias;
631
+ if (row.kind === "operation")
632
+ return row.event.aliases[0] ?? "";
633
+ return row.item.alias ?? row.item.host ?? "";
634
+ }
635
+ function activityRows(model) {
636
+ const rows = [];
637
+ for (const group of model.activity) {
638
+ if (!isTerminalDeliveryState(group.state))
639
+ continue;
640
+ rows.push({ kind: "delivery", timestamp: group.timestamp, group });
641
+ }
642
+ for (const item of model.attention) {
643
+ if (item.timestamp === undefined)
644
+ continue;
645
+ rows.push({
646
+ kind: "alert",
647
+ timestamp: item.timestamp,
648
+ item,
649
+ guidanceKey: guidanceCopyKey(item.guidance),
650
+ });
651
+ }
652
+ for (const event of model.brokerActivity ?? []) {
653
+ rows.push({ kind: "operation", timestamp: event.timestamp, event });
654
+ }
655
+ return rows.sort((left, right) => compareText(right.timestamp ?? "", left.timestamp ?? "") ||
656
+ compareText(activityRowSource(left), activityRowSource(right)));
657
+ }
658
+ Embassy.activityRows = activityRows;
659
+ function diagnosticsProps(model) {
660
+ return {
661
+ connectors: model.connectors,
662
+ connectorsOmitted: model.omissions.connectors,
663
+ compatibilityChecks: model.compatibilityChecks ?? [],
664
+ expiredCount: model.accounting.expired,
665
+ ...(model.deadlinePressure === undefined
666
+ ? {}
667
+ : { deadlinePressure: model.deadlinePressure }),
668
+ queuedMessages: model.exchange.queuedMessages,
669
+ queueCountIsLowerBound: model.exchange.queueCountIsLowerBound,
670
+ accounting: model.accounting,
671
+ omissions: model.omissions,
672
+ };
673
+ }
674
+ Embassy.diagnosticsProps = diagnosticsProps;
675
+ Embassy.adapter = {
676
+ overviewProps,
677
+ deliveriesGroups,
678
+ routesProps,
679
+ activityRows,
680
+ diagnosticsProps,
681
+ queueSplit,
682
+ routeOldestAgeMs,
683
+ pulse,
684
+ worstConnectorHealth,
685
+ worstCompatibility,
686
+ extractSuccessions,
687
+ isMonitorOnly,
688
+ hasLifecycleTruncation,
689
+ deliveriesTruncated,
690
+ deliveryGroupKey,
691
+ guidanceCopyKey,
692
+ attentionCommand,
693
+ attentionViews,
694
+ isTerminalDeliveryState,
695
+ parseTimestampMs,
696
+ };
697
+ })(Embassy || (Embassy = {}));
698
+ var Embassy;
699
+ (function (Embassy) {
700
+ const HEARTBEAT_WATCHDOG_MS = 35_000;
701
+ const AUTO_RECONNECT_DELAY_MS = 5_000;
702
+ const MAX_AUTO_RECONNECT_ATTEMPTS = 3;
703
+ const MAX_STREAM_BUFFER_LENGTH = 1_048_576;
704
+ function createProtocol(options) {
705
+ const base = location.pathname.endsWith("/bootstrap")
706
+ ? location.pathname.slice(0, -10)
707
+ : location.pathname.endsWith("/")
708
+ ? location.pathname.slice(0, -1)
709
+ : location.pathname;
710
+ const api = (name) => base + "/" + name;
711
+ let connectionState = "connecting";
712
+ let controller;
713
+ let watchdogTimer;
714
+ let reconnectTimer;
715
+ let reconnectAttempts = 0;
716
+ let lastStreamRevision;
717
+ let gapRecoveryInFlight = false;
718
+ function apiFetch(name, fetchOptions = {}) {
719
+ const init = {
720
+ method: "POST",
721
+ credentials: "same-origin",
722
+ cache: "no-store",
723
+ headers: { "X-Embassy-Request": "1", ...(fetchOptions.headers ?? {}) },
724
+ };
725
+ if (fetchOptions.body !== undefined) {
726
+ init.body = fetchOptions.body;
727
+ }
728
+ if (fetchOptions.signal !== undefined) {
729
+ init.signal = fetchOptions.signal;
730
+ }
731
+ return fetch(api(name), init);
732
+ }
733
+ function notify(kind) {
734
+ options.onNotice?.(kind);
735
+ }
736
+ function setConnectionState(next) {
737
+ connectionState = next;
738
+ if (next === "connected") {
739
+ reconnectAttempts = 0;
740
+ }
741
+ options.onConnectionState(next);
742
+ if (next === "disconnected") {
743
+ scheduleAutoReconnect();
744
+ }
745
+ }
746
+ function clearWatchdog() {
747
+ if (watchdogTimer !== undefined) {
748
+ window.clearTimeout(watchdogTimer);
749
+ watchdogTimer = undefined;
750
+ }
751
+ }
752
+ function armWatchdog(current) {
753
+ clearWatchdog();
754
+ watchdogTimer = window.setTimeout(() => {
755
+ watchdogTimer = undefined;
756
+ if (connectionState !== "connected" || controller !== current) {
757
+ return;
758
+ }
759
+ current.abort();
760
+ setConnectionState("disconnected");
761
+ }, HEARTBEAT_WATCHDOG_MS);
762
+ }
763
+ function clearReconnectTimer() {
764
+ if (reconnectTimer !== undefined) {
765
+ window.clearTimeout(reconnectTimer);
766
+ reconnectTimer = undefined;
767
+ }
768
+ }
769
+ function scheduleAutoReconnect() {
770
+ if (reconnectTimer !== undefined || reconnectAttempts >= MAX_AUTO_RECONNECT_ATTEMPTS) {
771
+ return;
772
+ }
773
+ reconnectTimer = window.setTimeout(() => {
774
+ reconnectTimer = undefined;
775
+ if (connectionState !== "disconnected") {
776
+ return;
777
+ }
778
+ reconnectAttempts += 1;
779
+ void connect();
780
+ }, AUTO_RECONNECT_DELAY_MS);
781
+ }
782
+ function ingest(event, source) {
783
+ const previous = lastStreamRevision;
784
+ const gap = previous !== undefined && event.streamRevision > previous + 1;
785
+ lastStreamRevision = event.streamRevision;
786
+ options.onEvent(event);
787
+ if (event.reset) {
788
+ notify("reset");
789
+ }
790
+ if (gap) {
791
+ notify("missedFrames");
792
+ if (source === "stream" && !gapRecoveryInFlight) {
793
+ gapRecoveryInFlight = true;
794
+ void fetchSnapshotNow().finally(() => {
795
+ gapRecoveryInFlight = false;
796
+ });
797
+ }
798
+ }
799
+ }
800
+ async function exchangeCapability() {
801
+ const capability = location.hash.slice(1);
802
+ if (!capability) {
803
+ return;
804
+ }
805
+ history.replaceState(null, "", location.pathname);
806
+ const response = await apiFetch("session", {
807
+ headers: { "Content-Type": "text/plain;charset=UTF-8" },
808
+ body: capability,
809
+ });
810
+ if (!response.ok) {
811
+ throw new Error("session");
812
+ }
813
+ }
814
+ function consumeSseBlock(block) {
815
+ let event = "message";
816
+ const data = [];
817
+ for (const line of block.split("\n")) {
818
+ if (line.startsWith("event:")) {
819
+ event = line.slice(6).trim();
820
+ }
821
+ if (line.startsWith("data:")) {
822
+ data.push(line.slice(5).trimStart());
823
+ }
824
+ }
825
+ if (event === "snapshot" && data.length > 0) {
826
+ const parsed = JSON.parse(data.join("\n"));
827
+ setConnectionState("connected");
828
+ ingest(parsed, "stream");
829
+ }
830
+ else if (event === "observer_unavailable") {
831
+ setConnectionState("unavailable");
832
+ }
833
+ else if (event === "shutdown") {
834
+ setConnectionState("stopped");
835
+ }
836
+ }
837
+ async function connect() {
838
+ if (controller) {
839
+ controller.abort();
840
+ }
841
+ const current = new AbortController();
842
+ controller = current;
843
+ setConnectionState("connecting");
844
+ try {
845
+ const response = await apiFetch("stream", { signal: current.signal });
846
+ if (!response.ok || !response.body) {
847
+ throw new Error("stream");
848
+ }
849
+ const reader = response.body.getReader();
850
+ const decoder = new TextDecoder();
851
+ let buffer = "";
852
+ armWatchdog(current);
853
+ while (true) {
854
+ const result = await reader.read();
855
+ if (result.done) {
856
+ break;
857
+ }
858
+ armWatchdog(current);
859
+ buffer += decoder.decode(result.value, { stream: true }).replaceAll("\r\n", "\n");
860
+ if (buffer.length > MAX_STREAM_BUFFER_LENGTH) {
861
+ throw new Error("frame");
862
+ }
863
+ let boundary;
864
+ while ((boundary = buffer.indexOf("\n\n")) >= 0) {
865
+ consumeSseBlock(buffer.slice(0, boundary));
866
+ buffer = buffer.slice(boundary + 2);
867
+ }
868
+ }
869
+ if (!current.signal.aborted && connectionState !== "stopped") {
870
+ setConnectionState("disconnected");
871
+ }
872
+ }
873
+ catch {
874
+ if (!current.signal.aborted) {
875
+ setConnectionState("disconnected");
876
+ }
877
+ }
878
+ finally {
879
+ clearWatchdog();
880
+ }
881
+ }
882
+ async function fetchSnapshotNow() {
883
+ try {
884
+ const response = await apiFetch("snapshot");
885
+ if (response.ok) {
886
+ const event = (await response.json());
887
+ ingest(event, "snapshot");
888
+ }
889
+ else {
890
+ setConnectionState(connectionState === "connected" ? "unavailable" : "disconnected");
891
+ }
892
+ }
893
+ catch {
894
+ setConnectionState(connectionState === "connected" ? "unavailable" : "disconnected");
895
+ }
896
+ }
897
+ function isActionResult(value) {
898
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
899
+ return false;
900
+ }
901
+ const record = value;
902
+ return (Object.keys(record).length === 2 &&
903
+ typeof record.ok === "boolean" &&
904
+ typeof record.code === "string" &&
905
+ /^(?:ok|not_found|conflict|route_mismatch|busy|unavailable|rejected|rate_limited)$/u.test(record.code) &&
906
+ ((record.ok && record.code === "ok") ||
907
+ (!record.ok && record.code !== "ok")));
908
+ }
909
+ async function executeAction(action) {
910
+ let result = {
911
+ ok: false,
912
+ code: "unavailable",
913
+ };
914
+ try {
915
+ const response = await apiFetch("action", {
916
+ headers: { "Content-Type": "application/json" },
917
+ body: JSON.stringify(action),
918
+ });
919
+ const parsed = await response.json();
920
+ if (isActionResult(parsed))
921
+ result = parsed;
922
+ }
923
+ catch {
924
+ }
925
+ await fetchSnapshotNow();
926
+ return result;
927
+ }
928
+ async function start() {
929
+ try {
930
+ await exchangeCapability();
931
+ }
932
+ catch {
933
+ setConnectionState("disconnected");
934
+ return;
935
+ }
936
+ await connect();
937
+ }
938
+ return {
939
+ start: () => {
940
+ void start();
941
+ },
942
+ pause: () => {
943
+ clearReconnectTimer();
944
+ if (controller) {
945
+ controller.abort();
946
+ }
947
+ setConnectionState("paused");
948
+ },
949
+ reconnect: () => {
950
+ clearReconnectTimer();
951
+ reconnectAttempts = 0;
952
+ void connect();
953
+ },
954
+ readNow: () => {
955
+ void fetchSnapshotNow();
956
+ },
957
+ executeAction,
958
+ };
959
+ }
960
+ Embassy.createProtocol = createProtocol;
961
+ })(Embassy || (Embassy = {}));
962
+ var Embassy;
963
+ (function (Embassy) {
964
+ function StateChip(props) {
965
+ const t = Embassy.useT();
966
+ const domain = props.domain ?? "delivery";
967
+ const kind = domain === "delivery"
968
+ ? Embassy.chipKindFor(props.state, props.direction, props.safeErrorCode)
969
+ : Embassy.chipKindByDomain(domain, props.state, props.direction);
970
+ const meaning = props.note ??
971
+ t(Embassy.meaningKeyFor(domain, props.state, props.safeErrorCode, props.direction));
972
+ return (React.createElement("span", { className: props.small === true ? "chip chip--small" : "chip", "data-kind": kind, title: meaning, "aria-description": meaning }, props.label ?? props.state));
973
+ }
974
+ Embassy.StateChip = StateChip;
975
+ function MonoLabel(props) {
976
+ const className = props.className === undefined
977
+ ? "mono-label"
978
+ : `mono-label ${props.className}`;
979
+ return React.createElement("div", { className: className }, props.children);
980
+ }
981
+ Embassy.MonoLabel = MonoLabel;
982
+ function CopyCmd(props) {
983
+ const t = Embassy.useT();
984
+ const [feedback, setFeedback] = React.useState("idle");
985
+ React.useEffect(() => {
986
+ if (feedback === "idle")
987
+ return undefined;
988
+ const handle = window.setTimeout(() => {
989
+ setFeedback("idle");
990
+ }, 1_500);
991
+ return () => {
992
+ window.clearTimeout(handle);
993
+ };
994
+ }, [feedback]);
995
+ const copy = () => {
996
+ const clipboard = navigator.clipboard;
997
+ if (clipboard === undefined) {
998
+ setFeedback("failed");
999
+ return;
1000
+ }
1001
+ clipboard.writeText(props.cmd).then(() => {
1002
+ setFeedback("copied");
1003
+ }, () => {
1004
+ setFeedback("failed");
1005
+ });
1006
+ };
1007
+ const status = feedback === "copied"
1008
+ ? t("app.copied")
1009
+ : feedback === "failed"
1010
+ ? t("app.copyFailed")
1011
+ : "";
1012
+ return (React.createElement("div", { className: "copy-cmd" },
1013
+ React.createElement("code", { className: "copy-cmd__code" }, props.cmd),
1014
+ React.createElement("span", { className: "copy-cmd__status", "data-tone": feedback === "failed" ? "error" : undefined, "aria-live": "polite" }, status),
1015
+ React.createElement("button", { type: "button", className: "copy-cmd__button", onClick: copy }, t("app.copy"))));
1016
+ }
1017
+ Embassy.CopyCmd = CopyCmd;
1018
+ function Rule(props) {
1019
+ const className = props.className === undefined ? "rule" : `rule ${props.className}`;
1020
+ return React.createElement("hr", { className: className });
1021
+ }
1022
+ Embassy.Rule = Rule;
1023
+ function FilterPill(props) {
1024
+ return (React.createElement("button", { type: "button", className: "pill", "aria-pressed": props.active, onClick: props.onClick }, props.children));
1025
+ }
1026
+ Embassy.FilterPill = FilterPill;
1027
+ function TimeAgo(props) {
1028
+ const t = Embassy.useT();
1029
+ const [locale] = Embassy.useLocale();
1030
+ const nowMs = Embassy.useNowMs();
1031
+ const absolute = Embassy.fmtAbs(props.iso, locale);
1032
+ const parsedMs = Date.parse(props.iso);
1033
+ const ageMs = Number.isFinite(parsedMs) ? nowMs - parsedMs : undefined;
1034
+ const relative = ageMs !== undefined && ageMs >= 3_600_000
1035
+ ? t("time.ago", { duration: Embassy.fmtAge(ageMs) })
1036
+ : Embassy.fmtRel(props.iso, nowMs, locale);
1037
+ return (React.createElement("span", { className: "time-ago", title: absolute },
1038
+ relative,
1039
+ React.createElement("span", { className: "sr-only" },
1040
+ " (",
1041
+ absolute,
1042
+ ")")));
1043
+ }
1044
+ Embassy.TimeAgo = TimeAgo;
1045
+ function Tooltip(props) {
1046
+ const descriptionId = React.useId();
1047
+ return (React.createElement("span", { className: "tooltip", tabIndex: 0, "data-tip": props.tip, "data-tip-wrap": props.wrap === true ? "" : undefined, "aria-describedby": descriptionId },
1048
+ props.children ?? React.createElement("span", { className: "info-dot" }, "i"),
1049
+ React.createElement("span", { className: "sr-only", id: descriptionId }, props.tip)));
1050
+ }
1051
+ Embassy.Tooltip = Tooltip;
1052
+ function AbsentFeature(props) {
1053
+ const t = Embassy.useT();
1054
+ return (React.createElement("section", { className: "absent-feature" },
1055
+ React.createElement(MonoLabel, null, t("app.notLanded.title")),
1056
+ React.createElement("h3", { className: "absent-feature__title" }, props.title),
1057
+ React.createElement("p", { className: "absent-feature__body" }, props.body),
1058
+ React.createElement("p", { className: "absent-feature__note" }, t("app.notLanded.body")),
1059
+ props.cmd === undefined ? null : React.createElement(CopyCmd, { cmd: props.cmd })));
1060
+ }
1061
+ Embassy.AbsentFeature = AbsentFeature;
1062
+ })(Embassy || (Embassy = {}));
1063
+ var Embassy;
1064
+ (function (Embassy) {
1065
+ const PULSE_BAR_BASE_PX = 6;
1066
+ const PULSE_BAR_RANGE_PX = 38;
1067
+ const QUEUE_DEPTH_COMMAND = "EMBASSY_MAX_QUEUE_MESSAGES=<n> embassy serve";
1068
+ const NEXT_ACTION_COPY_KEYS = {
1069
+ discover_claude: "next.discoverClaude",
1070
+ select_claude: "next.selectClaude",
1071
+ pair_routes: "next.pairRoutes",
1072
+ restore_claude: "next.restoreClaude",
1073
+ repair_claude_inventory: "next.repairClaude",
1074
+ register_codex: "next.registerCodex",
1075
+ restore_codex: "next.restoreCodex",
1076
+ none: "next.none",
1077
+ };
1078
+ function LowerBound(props) {
1079
+ const t = Embassy.useT();
1080
+ const sentence = t("app.lowerBound", { count: props.count });
1081
+ return (React.createElement("span", { className: "lower-bound", title: sentence },
1082
+ React.createElement("span", { "aria-hidden": "true" }, t("count.atLeast", { count: props.count })),
1083
+ React.createElement("span", { className: "sr-only" }, sentence)));
1084
+ }
1085
+ function StatusCard(props) {
1086
+ return (React.createElement("div", { className: "status-card" },
1087
+ React.createElement("div", { className: "status-card__head" },
1088
+ React.createElement("span", { className: "status-card__label" }, props.label),
1089
+ props.chip),
1090
+ props.detail === undefined ? null : (React.createElement("span", { className: "status-card__detail" }, props.detail))));
1091
+ }
1092
+ function HealthStatusCard(props) {
1093
+ const t = Embassy.useT();
1094
+ const health = props.health;
1095
+ if (health === undefined) {
1096
+ return (React.createElement(StatusCard, { label: props.label, detail: undefined, chip: React.createElement("span", { className: "chip chip--small", "data-kind": "inert" }, t("app.overview.connectorMissing")) }));
1097
+ }
1098
+ 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 }) }));
1099
+ }
1100
+ function CompatStatusCard(props) {
1101
+ const t = Embassy.useT();
1102
+ const compatibility = props.compatibility;
1103
+ if (compatibility === undefined) {
1104
+ return (React.createElement(StatusCard, { label: t("app.overview.compat"), detail: undefined, chip: React.createElement("span", { className: "chip chip--small", "data-kind": "inert" }, t("app.overview.connectorMissing")) }));
1105
+ }
1106
+ return (React.createElement(StatusCard, { label: t("app.overview.compat"), detail: t(`compatibility.meaning.${Embassy.camelCaseToken(compatibility)}`), chip: React.createElement(Embassy.StateChip, { domain: "compatibility", state: compatibility, small: true }) }));
1107
+ }
1108
+ function ExchangeNode(props) {
1109
+ const party = props.party;
1110
+ return (React.createElement("div", { className: "exchange-node" },
1111
+ React.createElement("div", null,
1112
+ React.createElement("div", { className: "exchange-node__title" }, props.title),
1113
+ React.createElement("div", { className: "exchange-node__sub" }, props.sub)),
1114
+ React.createElement("div", { className: "chip-row" },
1115
+ React.createElement(Embassy.StateChip, { domain: "party", state: party.status, small: true }),
1116
+ party.countIsLowerBound ? React.createElement(LowerBound, { count: party.total }) : null),
1117
+ React.createElement("div", { className: "exchange-node__line" }, props.countLine),
1118
+ party.primaryAlias === undefined ? null : (React.createElement("div", { className: "exchange-node__line mono" }, party.primaryAlias))));
1119
+ }
1120
+ function liveQueueAgeMs(queue, nowMs) {
1121
+ const queuedMs = Embassy.parseTimestampMs(queue.oldestQueuedAt);
1122
+ if (queuedMs === undefined)
1123
+ return queue.oldestAgeMs;
1124
+ return Math.max(0, nowMs - queuedMs);
1125
+ }
1126
+ function ExchangeEdge(props) {
1127
+ const t = Embassy.useT();
1128
+ const nowMs = Embassy.useNowMs();
1129
+ const queue = props.queue;
1130
+ const inbound = props.direction === "in";
1131
+ const ageMs = liveQueueAgeMs(queue, nowMs);
1132
+ const oldest = queue.depth > 0 && ageMs !== undefined
1133
+ ? ` · ${t("app.overview.oldest")} ${Embassy.fmtAge(ageMs)}`
1134
+ : "";
1135
+ return (React.createElement("div", { className: "exchange-edge" },
1136
+ React.createElement("div", { className: "edge-label" }, props.label),
1137
+ React.createElement("div", { className: "edge-line" },
1138
+ React.createElement("span", { className: inbound ? "edge-arrow edge-arrow--in" : "edge-arrow edge-arrow--out", "aria-hidden": "true" }, inbound ? "←" : "→")),
1139
+ React.createElement("div", { className: "edge-queue", "data-active": queue.depth > 0 ? "true" : "false" }, `${t("app.overview.depth")} ${queue.depth}${oldest}`),
1140
+ queue.depthIsLowerBound ? React.createElement(LowerBound, { count: queue.depth }) : null));
1141
+ }
1142
+ function attentionScopeParts(item, t) {
1143
+ const parts = [];
1144
+ if (item.provider !== undefined) {
1145
+ parts.push(item.provider === "claude" ? t("provider.claude") : t("provider.codex"));
1146
+ }
1147
+ if (item.alias !== undefined)
1148
+ parts.push(item.alias);
1149
+ if (item.host !== undefined)
1150
+ parts.push(item.host);
1151
+ if (item.code !== undefined)
1152
+ parts.push(item.code);
1153
+ return parts;
1154
+ }
1155
+ function AttentionItem(props) {
1156
+ const t = Embassy.useT();
1157
+ const { item, guidanceKey, command } = props.view;
1158
+ const scope = attentionScopeParts(item, t).join(" · ");
1159
+ return (React.createElement("div", { className: "attention-item" },
1160
+ React.createElement("div", { className: "attention-item__head" },
1161
+ React.createElement(Embassy.StateChip, { domain: "severity", state: item.severity, small: true }),
1162
+ React.createElement("h3", { className: "attention-item__title" }, t(`guidance.${guidanceKey}.title`)),
1163
+ item.timestamp === undefined ? null : (React.createElement(Embassy.TimeAgo, { iso: item.timestamp }))),
1164
+ React.createElement("p", { className: "attention-item__body" }, t(`guidance.${guidanceKey}.body`)),
1165
+ React.createElement("p", { className: "attention-item__body" }, t(`guidance.${guidanceKey}.action`)),
1166
+ scope === "" ? null : (React.createElement("div", { className: "attention-item__scope" }, `${t("attention.scope")}: ${scope}`)),
1167
+ React.createElement("div", { className: "attention-item__action" },
1168
+ React.createElement(Embassy.CopyCmd, { cmd: command })),
1169
+ 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));
1170
+ }
1171
+ function PulseBarButton(props) {
1172
+ const t = Embassy.useT();
1173
+ const { bar } = props;
1174
+ const viewIn = t("app.overview.viewIn");
1175
+ const heightPx = PULSE_BAR_BASE_PX + (PULSE_BAR_RANGE_PX * bar.count) / props.maxCount;
1176
+ return (React.createElement("button", { type: "button", className: "pulse-bar-btn", title: viewIn, "aria-label": `${bar.state}: ${bar.count} — ${viewIn}`, onClick: () => {
1177
+ props.onSelect(bar.state);
1178
+ } },
1179
+ React.createElement("span", { className: "pulse-count" }, bar.count),
1180
+ React.createElement("span", { className: "pulse-bar", "data-kind": Embassy.chipKindFor(bar.state), "data-empty": bar.count === 0 ? "true" : "false", style: { height: `${heightPx}px` } }),
1181
+ React.createElement(Embassy.StateChip, { state: bar.state, small: true })));
1182
+ }
1183
+ function OverviewTab(props) {
1184
+ const t = Embassy.useT();
1185
+ const { data, onViewDeliveries } = props;
1186
+ const strip = data.statusStrip;
1187
+ const queueOut = data.queueClaudeToCodex;
1188
+ const queueIn = data.queueCodexToClaude;
1189
+ const openInbound = data.inboundMode === "open";
1190
+ const showEdges = data.graph.readyPairCount > 0 || queueOut.depth > 0 || queueIn.depth > 0;
1191
+ const nextActionLine = (who, action) => action === "none"
1192
+ ? null
1193
+ : `${t("next.label")} · ${who}: ${t(NEXT_ACTION_COPY_KEYS[action])}`;
1194
+ const boardNotes = [
1195
+ !openInbound && showEdges && data.graph.readyPairCount === 0
1196
+ ? `${t("app.overview.noPair.title")} — ${t("app.overview.noPair.body")}`
1197
+ : null,
1198
+ nextActionLine(t("provider.claude"), data.exchange.claude.nextAction),
1199
+ nextActionLine(t("provider.codex"), data.exchange.codex.nextAction),
1200
+ ].filter((line) => line !== null);
1201
+ const showAttention = data.attention.length > 0 || data.attentionOmitted > 0;
1202
+ const maxPulse = data.pulse.bars.reduce((largest, bar) => Math.max(largest, bar.count), 1);
1203
+ const selectPulseState = React.useCallback((state) => {
1204
+ onViewDeliveries({ state });
1205
+ }, [onViewDeliveries]);
1206
+ const idPrefix = React.useId();
1207
+ const stripId = `${idPrefix}-strip`;
1208
+ const exchangeId = `${idPrefix}-exchange`;
1209
+ const attentionId = `${idPrefix}-attention`;
1210
+ const pulseId = `${idPrefix}-pulse`;
1211
+ return (React.createElement("div", { className: "tab-panel" },
1212
+ React.createElement("section", { className: "section", "aria-labelledby": stripId },
1213
+ React.createElement("h2", { className: "mono-label section-label", id: stripId }, t("app.overview.statusStrip")),
1214
+ React.createElement("div", { className: "status-strip" },
1215
+ React.createElement(HealthStatusCard, { label: t("app.overview.broker"), health: strip.broker }),
1216
+ React.createElement(HealthStatusCard, { label: t("app.overview.claudeConn"), health: strip.claudeConnector }),
1217
+ React.createElement(HealthStatusCard, { label: t("app.overview.codexConn"), health: strip.codexConnector }),
1218
+ React.createElement(CompatStatusCard, { compatibility: strip.compatibility }))),
1219
+ React.createElement("section", { className: "section", "aria-labelledby": exchangeId },
1220
+ React.createElement("div", { className: "row section-label section-head" },
1221
+ React.createElement("h2", { className: "mono-label", id: exchangeId }, t("exchange.eyebrow")),
1222
+ React.createElement(Embassy.Tooltip, { tip: t("exchange.note"), wrap: true },
1223
+ React.createElement("span", { className: "info-dot" }, "i"))),
1224
+ React.createElement("div", { className: "inbound-policy", "data-inbound-mode": data.inboundMode },
1225
+ React.createElement("span", { className: "chip chip--small", "data-kind": openInbound ? "warning" : "inert" }, t(openInbound
1226
+ ? "inbound.open.badge"
1227
+ : "inbound.paired.badge")),
1228
+ React.createElement("p", null, t(openInbound
1229
+ ? "inbound.open.body"
1230
+ : data.graph.readyPairCount > 0
1231
+ ? "inbound.paired.body"
1232
+ : "inbound.noPair.body"))),
1233
+ React.createElement("div", { className: "exchange-board" },
1234
+ React.createElement(ExchangeNode, { title: t("app.overview.node.claude.title"), sub: t("app.overview.node.claude.sub"), party: data.exchange.claude, countLine: t("exchange.count.claude", {
1235
+ ready: data.exchange.claude.ready,
1236
+ selectable: data.exchange.claude.selectable ?? "—",
1237
+ total: data.exchange.claude.total,
1238
+ }) }),
1239
+ showEdges ? (React.createElement("div", { className: "exchange-edges" },
1240
+ React.createElement(ExchangeEdge, { direction: "out", label: t("app.overview.queueC2x"), queue: queueOut }),
1241
+ React.createElement(ExchangeEdge, { direction: "in", label: t("app.overview.queueX2c"), queue: queueIn }))) : (React.createElement("div", { className: "no-pair" },
1242
+ React.createElement("span", { className: "no-pair__label" }, t(openInbound
1243
+ ? "inbound.open.badge"
1244
+ : "app.overview.noPair.title")),
1245
+ React.createElement("span", { className: "no-pair__body" }, t(openInbound
1246
+ ? "inbound.open.body"
1247
+ : "app.overview.noPair.body")))),
1248
+ React.createElement(ExchangeNode, { title: t("app.overview.node.codex.title"), sub: t("app.overview.node.codex.sub"), party: data.exchange.codex, countLine: t("app.overview.count.codex", {
1249
+ ready: data.exchange.codex.ready,
1250
+ total: data.exchange.codex.total,
1251
+ monitorOnly: data.exchange.codex.monitorOnly ?? 0,
1252
+ }) })),
1253
+ boardNotes.length === 0 ? null : (React.createElement("div", { className: "exchange-notes stack-sm" }, boardNotes.map((line) => (React.createElement("p", { className: "text-xs text-body-muted", key: line }, line)))))),
1254
+ showAttention ? (React.createElement("section", { className: "section", "aria-labelledby": attentionId },
1255
+ React.createElement("div", { className: "row section-label section-head" },
1256
+ React.createElement("h2", { className: "mono-label", id: attentionId }, t("attention.eyebrow")),
1257
+ data.attentionOmitted > 0 ? (React.createElement(LowerBound, { count: data.attention.length })) : null),
1258
+ 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 })))),
1259
+ data.attentionOmitted > 0 ? (React.createElement("p", { className: "footnote" }, t("attention.projectionOnly"))) : null)) : null,
1260
+ React.createElement("section", { className: "section", "aria-labelledby": pulseId },
1261
+ React.createElement("h2", { className: "mono-label section-label", id: pulseId }, t("app.overview.pulse.title")),
1262
+ data.pulse.total === 0 ? (React.createElement("p", { className: "pulse-empty" }, t("app.overview.pulse.empty"))) : null,
1263
+ React.createElement("div", { className: "pulse" }, data.pulse.bars.map((bar) => (React.createElement(PulseBarButton, { key: bar.state, bar: bar, maxCount: maxPulse, onSelect: selectPulseState })))),
1264
+ React.createElement("p", { className: "pulse-caption" },
1265
+ t("app.overview.pulse.caption"),
1266
+ data.pulse.isLowerBound ? " " : null,
1267
+ data.pulse.isLowerBound ? (React.createElement(LowerBound, { count: data.pulse.total })) : null))));
1268
+ }
1269
+ Embassy.OverviewTab = OverviewTab;
1270
+ })(Embassy || (Embassy = {}));
1271
+ var Embassy;
1272
+ (function (Embassy) {
1273
+ const DELIVERY_STATE_FILTERS = [
1274
+ "queued",
1275
+ "dispatching",
1276
+ "transport_written",
1277
+ "held",
1278
+ "duplicate",
1279
+ "delivered",
1280
+ "unconfirmed",
1281
+ "ambiguous",
1282
+ "failed",
1283
+ "expired",
1284
+ "rejected",
1285
+ "cancelled",
1286
+ "abandoned",
1287
+ ];
1288
+ const DELIVERY_STATE_FILTERS_COVER_THE_UNION = true;
1289
+ const DIRECTION_FILTERS = [
1290
+ "claude_to_codex",
1291
+ "codex_to_claude",
1292
+ ];
1293
+ const DIRECTION_COPY_KEYS = {
1294
+ claude_to_codex: "direction.claudeToCodex",
1295
+ codex_to_claude: "direction.codexToClaude",
1296
+ };
1297
+ const DIRECTION_PILL_COPY_KEYS = {
1298
+ claude_to_codex: "direction.claudeToCodex",
1299
+ codex_to_claude: "app.deliveries.dir.codexToClaude",
1300
+ };
1301
+ const SEND_TO_CODEX_CMD = "embassy send-to-codex --from <alias> --to <alias>";
1302
+ const countFormatters = new Map();
1303
+ function fmtCount(locale, value) {
1304
+ const existing = countFormatters.get(locale);
1305
+ if (existing !== undefined)
1306
+ return existing.format(value);
1307
+ const formatter = new Intl.NumberFormat(locale);
1308
+ countFormatters.set(locale, formatter);
1309
+ return formatter.format(value);
1310
+ }
1311
+ function includesFold(haystack, needleLower) {
1312
+ return haystack !== undefined && haystack.toLowerCase().includes(needleLower);
1313
+ }
1314
+ function equalsFold(value, wantedLower) {
1315
+ return value !== undefined && value.toLowerCase() === wantedLower;
1316
+ }
1317
+ function matchesQuery(view, needleLower) {
1318
+ if (needleLower === "")
1319
+ return true;
1320
+ const group = view.group;
1321
+ return (includesFold(group.messageIdSuffix, needleLower) ||
1322
+ includesFold(group.conversationIdSuffix, needleLower) ||
1323
+ includesFold(group.sourceAlias, needleLower) ||
1324
+ includesFold(group.targetAlias, needleLower) ||
1325
+ includesFold(group.safeErrorCode, needleLower) ||
1326
+ group.events.some((event) => includesFold(event.safeErrorCode, needleLower)));
1327
+ }
1328
+ function findPresetTarget(groups, token) {
1329
+ const wanted = token.trim().toLowerCase();
1330
+ if (wanted === "")
1331
+ return undefined;
1332
+ const bySuffix = groups.find((view) => equalsFold(view.group.messageIdSuffix, wanted) ||
1333
+ equalsFold(view.group.conversationIdSuffix, wanted));
1334
+ if (bySuffix !== undefined)
1335
+ return bySuffix;
1336
+ return groups.find((view) => equalsFold(view.group.safeErrorCode, wanted) ||
1337
+ view.group.events.some((event) => equalsFold(event.safeErrorCode, wanted)));
1338
+ }
1339
+ function groupByRoutePair(views) {
1340
+ const order = [];
1341
+ const buckets = new Map();
1342
+ for (const view of views) {
1343
+ const bucket = buckets.get(view.routePair);
1344
+ if (bucket === undefined) {
1345
+ buckets.set(view.routePair, [view]);
1346
+ order.push(view.routePair);
1347
+ }
1348
+ else {
1349
+ bucket.push(view);
1350
+ }
1351
+ }
1352
+ return order.map((pair) => ({ pair, rows: buckets.get(pair) ?? [] }));
1353
+ }
1354
+ function droppedEventCount(group) {
1355
+ let dropped = 0;
1356
+ for (let index = 1; index < group.events.length; index += 1) {
1357
+ const previous = group.events[index - 1];
1358
+ const current = group.events[index];
1359
+ if (previous === undefined || current === undefined)
1360
+ continue;
1361
+ const gap = current.sequence - previous.sequence - 1;
1362
+ if (gap > 0)
1363
+ dropped += gap;
1364
+ }
1365
+ return dropped;
1366
+ }
1367
+ function DetailRow(props) {
1368
+ return (React.createElement("div", { className: "detail-row" },
1369
+ React.createElement("span", { className: "detail-label" }, props.label),
1370
+ " ",
1371
+ React.createElement("span", { className: props.mono === true
1372
+ ? "detail-value detail-value--mono"
1373
+ : "detail-value" }, props.children)));
1374
+ }
1375
+ function DeliveryLifecycle(props) {
1376
+ const t = Embassy.useT();
1377
+ const [locale] = Embassy.useLocale();
1378
+ const events = props.group.events;
1379
+ return (React.createElement("div", { className: "lifecycle" }, events.map((event, index) => {
1380
+ const isLast = index === events.length - 1;
1381
+ const kind = Embassy.chipKindFor(event.state, props.group.direction, event.safeErrorCode);
1382
+ const noteParts = [];
1383
+ if (event.safeErrorCode !== undefined) {
1384
+ noteParts.push(event.safeErrorCode);
1385
+ }
1386
+ if (event.latencyMs !== undefined) {
1387
+ noteParts.push(`${fmtCount(locale, event.latencyMs)} ms`);
1388
+ }
1389
+ return (React.createElement("div", { className: "lifecycle-item", key: event.sequence },
1390
+ React.createElement("div", { className: "lifecycle-rail", "aria-hidden": true },
1391
+ React.createElement("span", { className: "lifecycle-dot", "data-kind": isLast ? kind : undefined }),
1392
+ isLast ? null : React.createElement("span", { className: "lifecycle-connector" })),
1393
+ React.createElement("div", { className: "lifecycle-body" },
1394
+ React.createElement("div", { className: "lifecycle-head" },
1395
+ React.createElement(Embassy.StateChip, { small: true, state: event.state, direction: props.group.direction, safeErrorCode: event.safeErrorCode }),
1396
+ event.timestamp === undefined ? (React.createElement("span", { className: "lifecycle-abs" }, t("time.unavailable"))) : (React.createElement(React.Fragment, null,
1397
+ React.createElement(Embassy.TimeAgo, { iso: event.timestamp }),
1398
+ React.createElement("span", { className: "lifecycle-abs" }, Embassy.fmtAbs(event.timestamp, locale))))),
1399
+ noteParts.length === 0 ? null : (React.createElement("div", { className: "lifecycle-note" }, noteParts.join(" · "))),
1400
+ React.createElement("div", { className: "lifecycle-meaning" }, t(Embassy.deliveryMeaningKey(event.state, event.safeErrorCode, props.group.direction))))));
1401
+ })));
1402
+ }
1403
+ function DeliveryDetail(props) {
1404
+ const t = Embassy.useT();
1405
+ const [locale] = Embassy.useLocale();
1406
+ const [rawOpen, setRawOpen] = React.useState(false);
1407
+ const rawId = React.useId();
1408
+ const group = props.view.group;
1409
+ const dropped = droppedEventCount(group);
1410
+ const frames = group.events.filter((event) => event.safeErrorCode !== undefined);
1411
+ const earliest = group.events[0]?.timestamp;
1412
+ return (React.createElement("div", { className: "detail-pane" },
1413
+ React.createElement("div", { className: "detail-pane__head" },
1414
+ React.createElement("span", { className: "detail-pane__id" },
1415
+ group.messageIdSuffix ?? "—",
1416
+ group.conversationIdSuffix === undefined
1417
+ ? ""
1418
+ : ` · conv …${group.conversationIdSuffix}`,
1419
+ group.steer === true ? " · STEER" : ""),
1420
+ React.createElement("div", { className: "detail-pane__sub" },
1421
+ props.view.routePair,
1422
+ " \u00B7 ",
1423
+ t(DIRECTION_COPY_KEYS[group.direction]))),
1424
+ React.createElement("div", null,
1425
+ React.createElement(Embassy.MonoLabel, { className: "section-label section-label--lifecycle" }, t("app.deliveries.lifecycle")),
1426
+ React.createElement(DeliveryLifecycle, { group: group }),
1427
+ props.view.eventsTruncated ? (React.createElement("p", { className: "truncation-note" }, t("app.deliveries.eventsTruncated", {
1428
+ count: fmtCount(locale, dropped),
1429
+ }))) : null),
1430
+ React.createElement("div", null,
1431
+ React.createElement(Embassy.MonoLabel, { className: "section-label section-label--body" }, t("app.deliveries.bodyLabel")),
1432
+ React.createElement("p", { className: "detail-pane__sub" }, t("app.deliveries.bodiesNote"))),
1433
+ React.createElement("div", null,
1434
+ React.createElement(Embassy.MonoLabel, { className: "section-label section-label--frames" }, t("app.deliveries.frames")),
1435
+ 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 },
1436
+ React.createElement("span", { className: "frame-code" }, event.safeErrorCode),
1437
+ event.timestamp === undefined ? (React.createElement("span", { className: "lifecycle-abs" }, t("time.unavailable"))) : (React.createElement(Embassy.TimeAgo, { iso: event.timestamp })))))))),
1438
+ React.createElement("div", { className: "detail-list" },
1439
+ group.latencyMs === undefined ? null : (React.createElement(DetailRow, { label: t("activity.column.elapsed"), mono: true }, `${fmtCount(locale, group.latencyMs)} ms`)),
1440
+ React.createElement(DetailRow, { label: t("activity.column.size"), mono: true }, `${fmtCount(locale, group.bytes)} B`),
1441
+ React.createElement(DetailRow, { label: t("app.deliveries.earliestRetained") }, earliest === undefined ? (t("time.unavailable")) : (React.createElement(React.Fragment, null,
1442
+ React.createElement(Embassy.TimeAgo, { iso: earliest }),
1443
+ " \u00B7 ",
1444
+ Embassy.fmtAbs(earliest, locale)))),
1445
+ group.safeErrorCode === undefined ? null : (React.createElement(DetailRow, { label: t("column.issue"), mono: true }, group.safeErrorCode))),
1446
+ React.createElement("div", null,
1447
+ React.createElement("button", { type: "button", className: "toggle-link", "aria-expanded": rawOpen, "aria-controls": rawOpen ? rawId : undefined, onClick: () => {
1448
+ setRawOpen(!rawOpen);
1449
+ } }, rawOpen ? t("app.deliveries.hideRaw") : t("app.deliveries.raw")),
1450
+ rawOpen ? (React.createElement("pre", { className: "raw-json", id: rawId, tabIndex: 0 }, JSON.stringify(group, null, 2))) : null)));
1451
+ }
1452
+ function DeliveriesEmpty(props) {
1453
+ return (React.createElement("div", { className: "empty-state" },
1454
+ React.createElement("p", { className: "empty-state__text" }, props.text),
1455
+ React.createElement(Embassy.CopyCmd, { cmd: SEND_TO_CODEX_CMD })));
1456
+ }
1457
+ const WATCH_EVENT_COPY_KEYS = {
1458
+ opened: "watches.event.opened",
1459
+ nudge: "watches.event.nudge",
1460
+ worker_reported_complete: "watches.event.workerReportedComplete",
1461
+ capability_degraded: "watches.event.capabilityDegraded",
1462
+ done: "watches.event.done",
1463
+ unresponsive: "watches.event.unresponsive",
1464
+ endpoint_retired: "watches.event.endpointRetired",
1465
+ disabled: "watches.event.disabled",
1466
+ };
1467
+ function ProgressWatchRegister(props) {
1468
+ const t = Embassy.useT();
1469
+ const [locale] = Embassy.useLocale();
1470
+ return (React.createElement("section", { className: "watch-register", "aria-labelledby": "watch-register-title" },
1471
+ React.createElement("div", { className: "row-baseline section-label" },
1472
+ React.createElement("div", null,
1473
+ React.createElement(Embassy.MonoLabel, null, t("watches.eyebrow")),
1474
+ React.createElement("h2", { id: "watch-register-title" }, t("watches.title"))),
1475
+ React.createElement("span", { className: "mono text-muted" }, fmtCount(locale, props.watches.length))),
1476
+ React.createElement("p", { className: "footnote" }, t("watches.note")),
1477
+ 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", "data-phase": watch.phase, key: `${watch.ownerAlias}|${watch.workerAlias}|${watch.conversationIdSuffix}` },
1478
+ React.createElement("div", { className: "watch-card__head" },
1479
+ React.createElement("code", null,
1480
+ "\u2026",
1481
+ watch.conversationIdSuffix),
1482
+ React.createElement(Embassy.StateChip, { domain: "severity", state: watch.phase === "episode" ? "warning" : "info", label: t(watch.phase === "episode"
1483
+ ? "watches.phase.episode"
1484
+ : "watches.phase.quiet"), note: t(watch.phase === "episode"
1485
+ ? "watches.phase.episode"
1486
+ : "watches.phase.quiet") })),
1487
+ React.createElement("strong", null,
1488
+ watch.ownerAlias,
1489
+ " \u2192 ",
1490
+ watch.workerAlias),
1491
+ React.createElement("dl", { className: "watch-card__facts" },
1492
+ React.createElement("div", null,
1493
+ React.createElement("dt", null, t("watches.column.quietFor")),
1494
+ React.createElement("dd", null,
1495
+ React.createElement(Embassy.TimeAgo, { iso: watch.lastActivityAt }))),
1496
+ React.createElement("div", null,
1497
+ React.createElement("dt", null, t("watches.column.nextAction")),
1498
+ React.createElement("dd", null,
1499
+ React.createElement(Embassy.TimeAgo, { iso: watch.nextActionAt }))),
1500
+ React.createElement("div", null,
1501
+ React.createElement("dt", null, t("watches.column.nudges")),
1502
+ React.createElement("dd", null, fmtCount(locale, watch.nudgeCount))),
1503
+ React.createElement("div", null,
1504
+ React.createElement("dt", null, t("watches.column.capability")),
1505
+ React.createElement("dd", null, t(watch.capability === "route" ? "watches.capability.route" : "watches.capability.conversation")))),
1506
+ watch.workerReportedComplete ? (React.createElement("p", { className: "footnote" }, t("watches.workerComplete"))) : null))))),
1507
+ props.events.length === 0 ? null : (React.createElement("details", { className: "watch-history" },
1508
+ React.createElement("summary", null, t("watches.history.title")),
1509
+ React.createElement("ol", null, props.events.map((event) => (React.createElement("li", { key: event.sequence },
1510
+ React.createElement(Embassy.TimeAgo, { iso: event.timestamp }),
1511
+ " \u00B7 ",
1512
+ React.createElement("code", null,
1513
+ "\u2026",
1514
+ event.conversationIdSuffix),
1515
+ " \u00B7 ",
1516
+ t(WATCH_EVENT_COPY_KEYS[event.kind]),
1517
+ event.nudgeNumber === undefined ? "" : ` #${event.nudgeNumber}`))))))));
1518
+ }
1519
+ function DeliveriesTab(props) {
1520
+ const t = Embassy.useT();
1521
+ const [locale] = Embassy.useLocale();
1522
+ const captionId = React.useId();
1523
+ const searchId = React.useId();
1524
+ const [directionFilter, setDirectionFilter] = React.useState("all");
1525
+ const [stateFilter, setStateFilter] = React.useState("all");
1526
+ const [viewMode, setViewMode] = React.useState("byRoute");
1527
+ const [query, setQuery] = React.useState("");
1528
+ const [selectedKey, setSelectedKey] = React.useState(undefined);
1529
+ const rowRefs = React.useRef(new Map());
1530
+ const groups = props.groups;
1531
+ const preset = props.preset;
1532
+ const clearPreset = props.clearPreset;
1533
+ React.useEffect(() => {
1534
+ if (preset === undefined)
1535
+ return;
1536
+ if (preset.state !== undefined)
1537
+ setStateFilter(preset.state);
1538
+ const token = preset.token === undefined ? "" : preset.token.trim();
1539
+ if (token !== "") {
1540
+ setQuery(token);
1541
+ const target = findPresetTarget(groups, token);
1542
+ if (target !== undefined)
1543
+ setSelectedKey(target.key);
1544
+ }
1545
+ clearPreset?.();
1546
+ }, [preset]);
1547
+ const needle = query.trim().toLowerCase();
1548
+ const filtered = React.useMemo(() => groups.filter((view) => (directionFilter === "all" ||
1549
+ view.group.direction === directionFilter) &&
1550
+ (stateFilter === "all" || view.group.state === stateFilter) &&
1551
+ matchesQuery(view, needle)), [groups, directionFilter, stateFilter, needle]);
1552
+ const pairs = React.useMemo(() => groupByRoutePair(filtered), [filtered]);
1553
+ const ordered = React.useMemo(() => (viewMode === "flat" ? filtered : pairs.flatMap((pair) => pair.rows)), [viewMode, filtered, pairs]);
1554
+ const selectedView = ordered.find((view) => view.key === selectedKey);
1555
+ const activeKey = selectedView?.key ?? ordered[0]?.key;
1556
+ const moveFocus = (event) => {
1557
+ const keys = ordered.map((view) => view.key);
1558
+ if (keys.length === 0)
1559
+ return;
1560
+ const currentIndex = Math.max(0, activeKey === undefined ? 0 : keys.indexOf(activeKey));
1561
+ let nextIndex;
1562
+ switch (event.key) {
1563
+ case "ArrowDown":
1564
+ nextIndex = Math.min(keys.length - 1, currentIndex + 1);
1565
+ break;
1566
+ case "ArrowUp":
1567
+ nextIndex = Math.max(0, currentIndex - 1);
1568
+ break;
1569
+ case "Home":
1570
+ nextIndex = 0;
1571
+ break;
1572
+ case "End":
1573
+ nextIndex = keys.length - 1;
1574
+ break;
1575
+ default:
1576
+ return;
1577
+ }
1578
+ const nextKey = keys[nextIndex];
1579
+ if (nextKey === undefined)
1580
+ return;
1581
+ event.preventDefault();
1582
+ setSelectedKey(nextKey);
1583
+ rowRefs.current.get(nextKey)?.focus();
1584
+ };
1585
+ const renderRow = (view) => {
1586
+ const group = view.group;
1587
+ const isSelected = view.key === selectedView?.key;
1588
+ 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) => {
1589
+ if (element === null)
1590
+ rowRefs.current.delete(view.key);
1591
+ else
1592
+ rowRefs.current.set(view.key, element);
1593
+ }, onClick: () => {
1594
+ setSelectedKey(view.key);
1595
+ } },
1596
+ React.createElement("span", { className: "delivery-row__id" }, group.messageIdSuffix ?? "—"),
1597
+ React.createElement("span", { className: "delivery-row__route", title: view.routePair }, view.routePair),
1598
+ React.createElement(Embassy.StateChip, { small: true, state: group.state, direction: group.direction, safeErrorCode: group.safeErrorCode }),
1599
+ group.steer === true ? (React.createElement("span", { className: "mono text-muted" }, "STEER")) : null,
1600
+ React.createElement("span", { className: "delivery-row__time" }, group.timestamp === undefined ? (t("time.unavailable")) : (React.createElement(Embassy.TimeAgo, { iso: group.timestamp })))));
1601
+ };
1602
+ const list = ordered.length === 0 ? null : (React.createElement("div", { className: "delivery-list", role: "listbox", "aria-labelledby": captionId, onKeyDown: moveFocus }, viewMode === "flat"
1603
+ ? filtered.map(renderRow)
1604
+ : pairs.map((pair) => (React.createElement("div", { className: "delivery-group", role: "group", "aria-label": pair.pair, key: pair.pair },
1605
+ React.createElement("div", { className: "delivery-group__head", "aria-hidden": true },
1606
+ React.createElement(Embassy.MonoLabel, null, t("activity.column.route")),
1607
+ React.createElement("span", { className: "delivery-group__key" }, pair.pair)),
1608
+ pair.rows.map(renderRow),
1609
+ React.createElement(Embassy.Rule, null))))));
1610
+ return (React.createElement("div", { className: "deliveries-layout" },
1611
+ React.createElement("div", { className: "deliveries-main" },
1612
+ React.createElement("div", { className: "row-baseline section-label" },
1613
+ React.createElement("h2", { className: "text-xs text-muted", id: captionId }, t("app.deliveries.caption")),
1614
+ React.createElement("span", { className: "mono text-muted" }, t("attention.countVisible", {
1615
+ count: fmtCount(locale, ordered.length),
1616
+ })),
1617
+ props.omissions.messageGroups > 0 ? (React.createElement("span", { className: "lower-bound" }, t("app.lowerBound", {
1618
+ count: fmtCount(locale, groups.length),
1619
+ }))) : null),
1620
+ React.createElement("div", { className: "filters" },
1621
+ React.createElement("div", { className: "filter-group" },
1622
+ React.createElement(Embassy.MonoLabel, null, t("app.deliveries.dir.label")),
1623
+ React.createElement("div", { className: "pill-row", role: "group", "aria-label": t("app.deliveries.dir.label") },
1624
+ React.createElement(Embassy.FilterPill, { active: directionFilter === "all", onClick: () => {
1625
+ setDirectionFilter("all");
1626
+ } }, t("app.deliveries.dir.all")),
1627
+ DIRECTION_FILTERS.map((direction) => (React.createElement(Embassy.FilterPill, { key: direction, active: directionFilter === direction, onClick: () => {
1628
+ setDirectionFilter(direction);
1629
+ } }, t(DIRECTION_PILL_COPY_KEYS[direction])))))),
1630
+ React.createElement("div", { className: "filter-group" },
1631
+ React.createElement(Embassy.MonoLabel, null, t("app.deliveries.view.label")),
1632
+ React.createElement("div", { className: "pill-row", role: "group", "aria-label": t("app.deliveries.view.label") },
1633
+ React.createElement(Embassy.FilterPill, { active: viewMode === "byRoute", onClick: () => {
1634
+ setViewMode("byRoute");
1635
+ } }, t("app.deliveries.view.byRoute")),
1636
+ React.createElement(Embassy.FilterPill, { active: viewMode === "flat", onClick: () => {
1637
+ setViewMode("flat");
1638
+ } }, t("app.deliveries.view.flat"))))),
1639
+ React.createElement("p", { className: "footnote section-label" }, t("app.deliveries.noConv")),
1640
+ React.createElement(ProgressWatchRegister, { watches: props.watches, events: props.watchEvents }),
1641
+ React.createElement("div", { className: "filter-group filters-block" },
1642
+ React.createElement(Embassy.MonoLabel, null, t("column.state")),
1643
+ React.createElement("div", { className: "pill-row", role: "group", "aria-label": t("column.state") },
1644
+ React.createElement(Embassy.FilterPill, { active: stateFilter === "all", onClick: () => {
1645
+ setStateFilter("all");
1646
+ } }, t("app.deliveries.state.all")),
1647
+ DELIVERY_STATE_FILTERS.map((state) => (React.createElement(Embassy.FilterPill, { key: state, active: stateFilter === state, onClick: () => {
1648
+ setStateFilter(stateFilter === state ? "all" : state);
1649
+ } }, state))))),
1650
+ React.createElement("div", { className: "filter-group filters-search" },
1651
+ React.createElement("label", { className: "sr-only", htmlFor: searchId }, t("app.deliveries.search")),
1652
+ React.createElement("input", { id: searchId, className: "text-input filter-input", type: "text", value: query, placeholder: t("app.deliveries.search"), onChange: (event) => {
1653
+ setQuery(event.target.value);
1654
+ } })),
1655
+ props.omissions.messageEvents > 0 ? (React.createElement("p", { className: "footnote section-label" }, t("app.deliveries.eventsTruncated", {
1656
+ count: fmtCount(locale, props.omissions.messageEvents),
1657
+ }))) : null,
1658
+ 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,
1659
+ list,
1660
+ viewMode === "flat" ? React.createElement(Embassy.Rule, null) : null))),
1661
+ 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 })))));
1662
+ }
1663
+ Embassy.DeliveriesTab = DeliveriesTab;
1664
+ })(Embassy || (Embassy = {}));
1665
+ var Embassy;
1666
+ (function (Embassy) {
1667
+ const TOPOLOGY_ROW_HEIGHT_PX = 64;
1668
+ const TOPOLOGY_PADDING_PX = 18;
1669
+ const TOPOLOGY_EDGE_WIDTH_PX = 220;
1670
+ const SELECT_CLAUDE_COMMAND = "embassy select-claude --alias <alias>";
1671
+ const REGISTER_CODEX_COMMAND = "embassy register-codex --alias codex-<name>@<host>";
1672
+ const SUCCEEDS_COMMAND = "embassy register-codex --alias <new> --succeeds <old>";
1673
+ const ROUTE_COUNTER_ROWS = [
1674
+ ["accepted", "diagnostics.accepted"],
1675
+ ["delivered", "diagnostics.delivered"],
1676
+ ["unconfirmed", "diagnostics.unconfirmed"],
1677
+ ["failed", "diagnostics.failed"],
1678
+ ["ambiguous", "diagnostics.ambiguous"],
1679
+ ["expired", "diagnostics.expired"],
1680
+ ["cancelled", "diagnostics.cancelled"],
1681
+ ["abandoned", "diagnostics.abandoned"],
1682
+ ["rejected", "diagnostics.rejected"],
1683
+ ["bytesAccepted", "diagnostics.bytesAccepted"],
1684
+ ];
1685
+ const countFormatters = new Map();
1686
+ function formatCount(value, locale) {
1687
+ const existing = countFormatters.get(locale);
1688
+ if (existing !== undefined)
1689
+ return existing.format(value);
1690
+ const formatter = new Intl.NumberFormat(locale);
1691
+ countFormatters.set(locale, formatter);
1692
+ return formatter.format(value);
1693
+ }
1694
+ function isReadyCodexRoute(route) {
1695
+ return (route.enabled &&
1696
+ route.compatibility === "compatible" &&
1697
+ (route.state === "idle" || route.state === "busy"));
1698
+ }
1699
+ function rowIdentity(alias, host) {
1700
+ return `${alias}\u0000${host}`;
1701
+ }
1702
+ function topologyRowCenterY(index) {
1703
+ return (TOPOLOGY_PADDING_PX +
1704
+ index * TOPOLOGY_ROW_HEIGHT_PX +
1705
+ TOPOLOGY_ROW_HEIGHT_PX / 2);
1706
+ }
1707
+ function topologyEdges(data) {
1708
+ const claudeIndices = new Map(data.peers.map((peer, index) => [peer.alias, index]));
1709
+ const codexIndices = new Map(data.codexRoutes.map((view, index) => [view.route.alias, index]));
1710
+ const edges = [];
1711
+ for (const pair of data.pairs) {
1712
+ const claudeIndex = claudeIndices.get(pair.claudeAlias);
1713
+ const codexIndex = codexIndices.get(pair.codexAlias);
1714
+ if (claudeIndex === undefined || codexIndex === undefined)
1715
+ continue;
1716
+ edges.push({
1717
+ claudeIndex,
1718
+ codexIndex,
1719
+ claudeAlias: pair.claudeAlias,
1720
+ codexAlias: pair.codexAlias,
1721
+ state: pair.state,
1722
+ });
1723
+ }
1724
+ return edges;
1725
+ }
1726
+ function isEdgeEndpoint(edge, side, index) {
1727
+ return side === "claude"
1728
+ ? index === edge.claudeIndex
1729
+ : index === edge.codexIndex;
1730
+ }
1731
+ function chipHighlight(focus, edges, side, index) {
1732
+ if (focus === undefined)
1733
+ return undefined;
1734
+ if (focus.side === side && focus.index === index)
1735
+ return "on";
1736
+ if (focus.side !== side &&
1737
+ edges.some((edge) => isEdgeEndpoint(edge, focus.side, focus.index) &&
1738
+ isEdgeEndpoint(edge, side, index))) {
1739
+ return "on";
1740
+ }
1741
+ return "off";
1742
+ }
1743
+ function edgeHighlight(focus, edge) {
1744
+ if (focus === undefined)
1745
+ return undefined;
1746
+ return isEdgeEndpoint(edge, focus.side, focus.index) ? "on" : "off";
1747
+ }
1748
+ function TopologyColumn(props) {
1749
+ const { side, highlightAt, onFocusChange } = props;
1750
+ if (props.items.length === 0) {
1751
+ return (React.createElement("div", { className: "topology__col" },
1752
+ React.createElement("p", { className: "topology__empty" }, props.emptyText)));
1753
+ }
1754
+ return (React.createElement("div", { className: "topology__col" },
1755
+ React.createElement("div", { role: "list", "aria-label": props.listLabel }, props.items.map((item, index) => (React.createElement("div", { key: item.key, role: "listitem", className: side === "claude"
1756
+ ? "topo-row topo-row--left"
1757
+ : "topo-row topo-row--right", onMouseEnter: () => {
1758
+ onFocusChange({ side, index });
1759
+ }, onMouseLeave: () => {
1760
+ onFocusChange(undefined);
1761
+ } },
1762
+ React.createElement("span", { className: "topo-chip", "data-side": side, "data-candidate": item.candidate ? "true" : undefined, "data-highlight": highlightAt(side, index), tabIndex: 0, onFocus: () => {
1763
+ onFocusChange({ side, index });
1764
+ }, onBlur: () => {
1765
+ onFocusChange(undefined);
1766
+ } }, item.alias),
1767
+ React.createElement("span", { className: "sr-only" }, item.srLabel)))))));
1768
+ }
1769
+ function TopologyEdgeSvg(props) {
1770
+ const height = TOPOLOGY_PADDING_PX * 2 + props.rows * TOPOLOGY_ROW_HEIGHT_PX;
1771
+ return (React.createElement("svg", { className: "topology__svg", width: TOPOLOGY_EDGE_WIDTH_PX, height: height, viewBox: `0 0 ${TOPOLOGY_EDGE_WIDTH_PX} ${height}`, "aria-hidden": "true", focusable: "false" }, props.edges.map((edge) => {
1772
+ const highlight = edgeHighlight(props.focus, edge);
1773
+ const stroke = highlight === "on"
1774
+ ? props.focusSide === "codex"
1775
+ ? "var(--action-blue)"
1776
+ : "var(--coral-accent, var(--coral))"
1777
+ : edge.state === "ready"
1778
+ ? "var(--hairline)"
1779
+ : "var(--warning)";
1780
+ return (React.createElement("path", { key: `${edge.claudeAlias}\0${edge.codexAlias}`, fill: "none", d: `M 0 ${topologyRowCenterY(edge.claudeIndex)} C 80 ${topologyRowCenterY(edge.claudeIndex)} 140 ${topologyRowCenterY(edge.codexIndex)} ${TOPOLOGY_EDGE_WIDTH_PX} ${topologyRowCenterY(edge.codexIndex)}`, style: {
1781
+ stroke,
1782
+ strokeDasharray: edge.state === "ready" ? undefined : "5 4",
1783
+ strokeWidth: highlight === "on" ? 2 : 1.25,
1784
+ opacity: highlight === "off" ? 0.2 : 1,
1785
+ } }));
1786
+ })));
1787
+ }
1788
+ function DetailRow(props) {
1789
+ return (React.createElement("p", { className: "detail-row" },
1790
+ React.createElement("span", { className: "detail-label" },
1791
+ props.label,
1792
+ ": "),
1793
+ props.children));
1794
+ }
1795
+ function AbsentDetail(props) {
1796
+ const t = Embassy.useT();
1797
+ return (React.createElement("p", { className: "detail-row" },
1798
+ React.createElement("span", { className: "detail-value detail-value--mono" }, props.fields.join(" · ")),
1799
+ React.createElement("span", { className: "detail-label" },
1800
+ " \u2014 ",
1801
+ t("app.routes.detail.absent"))));
1802
+ }
1803
+ function LowerBoundBadge(props) {
1804
+ const t = Embassy.useT();
1805
+ return (React.createElement("span", { className: "lower-bound" }, t("app.lowerBound", { count: props.count })));
1806
+ }
1807
+ function AccordionRow(props) {
1808
+ const t = Embassy.useT();
1809
+ const baseId = React.useId();
1810
+ const buttonId = `${baseId}-summary`;
1811
+ const panelId = `${baseId}-panel`;
1812
+ return (React.createElement("div", { className: "accordion" },
1813
+ React.createElement("button", { type: "button", id: buttonId, className: "accordion__row", "aria-label": t(props.open
1814
+ ? "app.routes.collapseDetails"
1815
+ : "app.routes.expandDetails", { alias: props.alias }), "aria-expanded": props.open, "aria-controls": props.open ? panelId : undefined, onClick: props.onToggle },
1816
+ React.createElement("span", { className: "accordion__alias" }, props.alias),
1817
+ props.head,
1818
+ React.createElement("span", { className: "accordion__marker", "aria-hidden": "true" }, props.open ? "−" : "+")),
1819
+ props.open ? (React.createElement("div", { className: "accordion__panel", id: panelId, role: "region", "aria-labelledby": buttonId },
1820
+ React.createElement("div", { className: "detail-list" }, props.children))) : null));
1821
+ }
1822
+ function PeerAccordionRow(props) {
1823
+ const t = Embassy.useT();
1824
+ const peer = props.peer;
1825
+ const selectionLabel = peer.selected
1826
+ ? t("status.selected")
1827
+ : peer.selectable
1828
+ ? t("status.available")
1829
+ : t("status.notSelectable");
1830
+ return (React.createElement(AccordionRow, { alias: peer.alias, open: props.open, onToggle: props.onToggle, head: React.createElement(React.Fragment, null,
1831
+ peer.selected ? (React.createElement("span", { className: "status-dot", title: t("status.selected"), "aria-hidden": "true" })) : null,
1832
+ React.createElement(Embassy.StateChip, { domain: "peer", state: peer.state, small: true }),
1833
+ peer.selected ? (React.createElement("span", { className: "chip chip--small", "data-kind": "positive" }, selectionLabel)) : null) },
1834
+ React.createElement(DetailRow, { label: t("column.state") },
1835
+ React.createElement(Embassy.StateChip, { domain: "peer", state: peer.state, small: true })),
1836
+ React.createElement(DetailRow, { label: t("column.compatibility") },
1837
+ React.createElement(Embassy.StateChip, { domain: "compatibility", state: peer.compatibility, small: true })),
1838
+ React.createElement(DetailRow, { label: t("column.validation") },
1839
+ React.createElement("span", { className: "chip chip--small", "data-kind": peer.validated ? "positive" : "warning" }, t(peer.validated
1840
+ ? "status.validated"
1841
+ : "status.validationRejected"))),
1842
+ React.createElement(DetailRow, { label: t("column.selection") },
1843
+ React.createElement("span", { className: "chip chip--small", "data-kind": peer.selected ? "positive" : "inert" }, selectionLabel)),
1844
+ peer.selectionGuidance === undefined ? null : (React.createElement("p", { className: "detail-row" }, t(`peer.reason.${Embassy.camelCaseToken(peer.selectionGuidance)}`))),
1845
+ React.createElement(DetailRow, { label: t("column.host") },
1846
+ React.createElement("span", { className: "detail-value detail-value--mono" }, peer.host)),
1847
+ React.createElement(DetailRow, { label: t("column.observed") }, peer.lastSeenAt === undefined ? (t("time.unavailable")) : (React.createElement(Embassy.TimeAgo, { iso: peer.lastSeenAt }))),
1848
+ peer.safeErrorCode === undefined ? null : (React.createElement(DetailRow, { label: t("column.issue") },
1849
+ React.createElement("span", { className: "detail-value detail-value--mono" }, peer.safeErrorCode))),
1850
+ React.createElement(AbsentDetail, { fields: ["binding", "renames", "discovery"] }),
1851
+ peer.selected || peer.selectable
1852
+ ? props.codexRoutes.map((view) => {
1853
+ const codexAlias = view.route.alias;
1854
+ const paired = props.pairs.some((pair) => pair.claudeAlias === peer.alias &&
1855
+ pair.codexAlias === codexAlias);
1856
+ return (React.createElement(ActionControl, { key: codexAlias, action: {
1857
+ action: paired ? "unpair" : "pair",
1858
+ claudeAlias: peer.alias,
1859
+ codexAlias,
1860
+ }, consequence: t(paired
1861
+ ? "app.routes.unpairCmd.consequence"
1862
+ : "app.routes.pairCmd.consequence", { claude: peer.alias, codex: codexAlias }), enabled: props.actionsEnabled, onAction: props.onAction }));
1863
+ })
1864
+ : null));
1865
+ }
1866
+ function RouteCountersTable(props) {
1867
+ const t = Embassy.useT();
1868
+ const [locale] = Embassy.useLocale();
1869
+ return (React.createElement("details", { className: "details-block" },
1870
+ React.createElement("summary", null, t("app.routes.counters")),
1871
+ React.createElement("div", { className: "details-block__content" },
1872
+ React.createElement("div", { className: "table-wrap" },
1873
+ React.createElement("table", { className: "data-table" },
1874
+ React.createElement("caption", { className: "table-caption" }, `${t("app.routes.counters")} — ${props.alias}`),
1875
+ React.createElement("tbody", null, ROUTE_COUNTER_ROWS.map(([field, copyKey]) => (React.createElement("tr", { key: field },
1876
+ React.createElement("th", { scope: "row" }, t(copyKey)),
1877
+ React.createElement("td", { className: "cell-mono" }, formatCount(props.counters[field], locale)))))))))));
1878
+ }
1879
+ function CodexAccordionRow(props) {
1880
+ const t = Embassy.useT();
1881
+ const [locale] = Embassy.useLocale();
1882
+ const route = props.view.route;
1883
+ return (React.createElement(AccordionRow, { alias: route.alias, open: props.open, onToggle: props.onToggle, head: React.createElement(React.Fragment, null,
1884
+ React.createElement("span", { className: "status-dot", title: t("app.routes.registered"), "aria-hidden": "true" }),
1885
+ React.createElement(Embassy.StateChip, { domain: "route", state: route.state, small: true }),
1886
+ props.view.monitorOnly ? (React.createElement("span", { className: "chip chip--small", "data-kind": "warning" }, t("app.routes.monitorOnly"))) : null) },
1887
+ React.createElement(DetailRow, { label: t("column.state") },
1888
+ React.createElement(Embassy.StateChip, { domain: "route", state: route.state, small: true })),
1889
+ React.createElement(DetailRow, { label: t("column.compatibility") },
1890
+ React.createElement(Embassy.StateChip, { domain: "compatibility", state: route.compatibility, small: true })),
1891
+ React.createElement(DetailRow, { label: t("column.selection") },
1892
+ React.createElement("span", { className: "chip chip--small", "data-kind": route.enabled ? "positive" : "inert" }, route.enabled ? t("status.enabled") : t("status.disabled"))),
1893
+ React.createElement(DetailRow, { label: t("app.routes.queueDepth") },
1894
+ React.createElement("span", { className: "detail-value detail-value--mono" }, formatCount(route.queueDepth, locale))),
1895
+ props.view.oldestAgeMs === undefined ? null : (React.createElement(DetailRow, { label: t("app.overview.oldest") },
1896
+ React.createElement("span", { className: "detail-value detail-value--mono" }, Embassy.fmtAge(props.view.oldestAgeMs)))),
1897
+ props.view.monitorOnly ? (React.createElement("p", { className: "detail-row" }, t("app.routes.monitorOnlyReason"))) : null,
1898
+ React.createElement(DetailRow, { label: t("column.host") },
1899
+ React.createElement("span", { className: "detail-value detail-value--mono" }, route.host)),
1900
+ React.createElement(DetailRow, { label: t("column.observed") }, route.lastSeenAt === undefined ? (t("time.unavailable")) : (React.createElement(Embassy.TimeAgo, { iso: route.lastSeenAt }))),
1901
+ route.safeErrorCode === undefined ? null : (React.createElement(DetailRow, { label: t("column.issue") },
1902
+ React.createElement("span", { className: "detail-value detail-value--mono" }, route.safeErrorCode))),
1903
+ React.createElement(AbsentDetail, { fields: ["policy", "identity lock", "registered"] }),
1904
+ React.createElement(RouteCountersTable, { alias: route.alias, counters: route.counters })));
1905
+ }
1906
+ function successionScope(item, t) {
1907
+ const parts = [];
1908
+ if (item.provider !== undefined)
1909
+ parts.push(t(`provider.${item.provider}`));
1910
+ if (item.alias !== undefined)
1911
+ parts.push(item.alias);
1912
+ if (item.host !== undefined)
1913
+ parts.push(item.host);
1914
+ if (item.code !== undefined)
1915
+ parts.push(item.code);
1916
+ return parts.join(" · ");
1917
+ }
1918
+ function SuccessionCard(props) {
1919
+ const t = Embassy.useT();
1920
+ const item = props.view.item;
1921
+ const scope = successionScope(item, t);
1922
+ return (React.createElement("article", { className: "succession-card" },
1923
+ React.createElement("div", { className: "succession-card__head" },
1924
+ React.createElement("div", { className: "chip-row" },
1925
+ React.createElement(Embassy.MonoLabel, null, "succession"),
1926
+ React.createElement(Embassy.StateChip, { domain: "severity", state: item.severity, small: true })),
1927
+ item.timestamp === undefined ? null : (React.createElement(Embassy.TimeAgo, { iso: item.timestamp }))),
1928
+ React.createElement("h3", { className: "card__title succession-card__title" }, t(`guidance.${props.view.guidanceKey}.title`)),
1929
+ React.createElement("p", { className: "card__body" }, t(`guidance.${props.view.guidanceKey}.body`)),
1930
+ scope === "" ? null : (React.createElement("p", { className: "attention-item__scope" },
1931
+ t("attention.scope"),
1932
+ ": ",
1933
+ scope)),
1934
+ React.createElement("p", { className: "card__body" }, t(`guidance.${props.view.guidanceKey}.action`)),
1935
+ React.createElement(AbsentDetail, { fields: ["predecessor", "successor", "drain outcome"] }),
1936
+ React.createElement("p", { className: "succession-note" }, t("app.routes.successions.note")),
1937
+ React.createElement(Embassy.CopyCmd, { cmd: props.view.command })));
1938
+ }
1939
+ function actionLabelKey(action) {
1940
+ switch (action.action) {
1941
+ case "pair":
1942
+ return "live.action.pair";
1943
+ case "unpair":
1944
+ return "live.action.unpair";
1945
+ case "refresh_dashboard":
1946
+ return "live.action.refresh";
1947
+ }
1948
+ }
1949
+ function ActionControl(props) {
1950
+ const t = Embassy.useT();
1951
+ const [confirming, setConfirming] = React.useState(false);
1952
+ const [pending, setPending] = React.useState(false);
1953
+ const [result, setResult] = React.useState(undefined);
1954
+ const actionIdentity = props.action.action === "refresh_dashboard"
1955
+ ? props.action.action
1956
+ : `${props.action.action}:${props.action.claudeAlias}:${props.action.codexAlias}`;
1957
+ React.useEffect(() => {
1958
+ setConfirming(false);
1959
+ setPending(false);
1960
+ setResult(undefined);
1961
+ }, [actionIdentity]);
1962
+ const baseLabel = t(actionLabelKey(props.action));
1963
+ const label = props.action.action === "refresh_dashboard"
1964
+ ? baseLabel
1965
+ : `${baseLabel}: ${props.action.claudeAlias} ↔ ${props.action.codexAlias}`;
1966
+ const confirm = async () => {
1967
+ if (!props.enabled || pending)
1968
+ return;
1969
+ setPending(true);
1970
+ setResult(undefined);
1971
+ const next = await props.onAction(props.action);
1972
+ setPending(false);
1973
+ setConfirming(false);
1974
+ setResult(next);
1975
+ };
1976
+ return (React.createElement("section", { className: "action-card", "aria-label": label },
1977
+ React.createElement("h3", { className: "action-card__title" }, label),
1978
+ React.createElement("p", { className: "action-card__consequence" }, props.consequence),
1979
+ !props.enabled ? (React.createElement("p", { className: "action-card__status", role: "status" }, t("live.action.requiresConnected"))) : null,
1980
+ result === undefined ? null : (React.createElement("p", { className: "action-card__status", "data-action-result": result.ok ? "success" : "failure", role: "status" }, t(result.ok ? "live.action.succeeded" : "live.action.failed", {
1981
+ code: result.code,
1982
+ }))),
1983
+ confirming ? (React.createElement("div", { className: "action-card__buttons", role: "group", "aria-label": label },
1984
+ React.createElement("button", { type: "button", className: "btn btn--primary", disabled: !props.enabled || pending, onClick: () => {
1985
+ void confirm();
1986
+ } }, pending ? t("live.action.pending") : t("live.action.confirm")),
1987
+ React.createElement("button", { type: "button", className: "btn btn--ghost", disabled: pending, onClick: () => {
1988
+ setConfirming(false);
1989
+ } }, t("live.action.cancel")))) : (React.createElement("button", { type: "button", className: "btn btn--ghost", disabled: !props.enabled, onClick: () => {
1990
+ setResult(undefined);
1991
+ setConfirming(true);
1992
+ } }, label))));
1993
+ }
1994
+ function RoutesTab(props) {
1995
+ const t = Embassy.useT();
1996
+ const data = props.data;
1997
+ const openInbound = data.inboundMode === "open";
1998
+ const [focus, setFocus] = React.useState(undefined);
1999
+ const [openPeer, setOpenPeer] = React.useState(undefined);
2000
+ const [openRoute, setOpenRoute] = React.useState(undefined);
2001
+ const edges = topologyEdges(data);
2002
+ const highlightAt = (side, index) => chipHighlight(focus, edges, side, index);
2003
+ const claudeItems = data.peers.map((peer, index) => ({
2004
+ key: `${rowIdentity(peer.alias, peer.host)}|${index}`,
2005
+ alias: peer.alias,
2006
+ candidate: !edges.some((edge) => edge.claudeAlias === peer.alias),
2007
+ srLabel: edges.some((edge) => edge.claudeAlias === peer.alias)
2008
+ ? t("status.selected")
2009
+ : peer.selectable
2010
+ ? t("status.available")
2011
+ : t("status.notSelectable"),
2012
+ }));
2013
+ const codexItems = data.codexRoutes.map((view, index) => ({
2014
+ key: `${rowIdentity(view.route.alias, view.route.host)}|${index}`,
2015
+ alias: view.route.alias,
2016
+ candidate: !edges.some((edge) => edge.codexAlias === view.route.alias),
2017
+ srLabel: t(`route.${Embassy.camelCaseToken(view.route.state)}`),
2018
+ }));
2019
+ return (React.createElement("div", { className: "tab-panel" },
2020
+ React.createElement("section", { className: "section", "aria-label": t("app.routes.topology") },
2021
+ React.createElement(Embassy.MonoLabel, { className: "section-label" }, t("app.routes.topology")),
2022
+ React.createElement("div", { className: "inbound-policy", "data-inbound-mode": data.inboundMode },
2023
+ React.createElement("span", { className: "chip chip--small", "data-kind": openInbound ? "warning" : "inert" }, t(openInbound
2024
+ ? "inbound.open.badge"
2025
+ : "inbound.paired.badge")),
2026
+ React.createElement("p", null, t(openInbound
2027
+ ? "inbound.open.body"
2028
+ : edges.length === 0
2029
+ ? "inbound.noPair.body"
2030
+ : "inbound.paired.body"))),
2031
+ React.createElement("div", { className: "topology" },
2032
+ React.createElement("div", { className: "topology__heads" },
2033
+ React.createElement(Embassy.MonoLabel, { className: "topology__head--claude" }, t("provider.claude")),
2034
+ React.createElement(Embassy.MonoLabel, { className: "topology__head--codex" }, t("provider.codex"))),
2035
+ React.createElement("div", { className: "topology__cols" },
2036
+ React.createElement(TopologyColumn, { side: "claude", items: claudeItems, listLabel: t("app.routes.candidates"), emptyText: t("app.routes.noPeers"), highlightAt: highlightAt, onFocusChange: setFocus }),
2037
+ React.createElement(TopologyEdgeSvg, { rows: Math.max(claudeItems.length, codexItems.length), edges: edges, focus: focus, focusSide: focus === undefined ? undefined : focus.side }),
2038
+ React.createElement(TopologyColumn, { side: "codex", items: codexItems, listLabel: t("app.routes.codexRoutes"), emptyText: t("app.routes.noCodex"), highlightAt: highlightAt, onFocusChange: setFocus })),
2039
+ edges.length === 0 ? (React.createElement("p", { className: "topology__empty" }, t(openInbound
2040
+ ? "inbound.open.body"
2041
+ : "app.routes.noPairInline"))) : (React.createElement("ul", { className: "topology__edge-list", "aria-label": t("app.routes.pairs") }, edges.map((edge) => (React.createElement("li", { key: `${edge.claudeAlias}\0${edge.codexAlias}` },
2042
+ t("app.routes.pairDescription", {
2043
+ claude: edge.claudeAlias,
2044
+ codex: edge.codexAlias,
2045
+ }),
2046
+ " ",
2047
+ React.createElement(Embassy.StateChip, { domain: "route", state: edge.state === "ready" ? "idle" : "stale", small: true })))))),
2048
+ data.pairsOmitted > 0 ? (React.createElement("p", { className: "topology__empty" }, t("app.omitted.pairs", { count: data.pairsOmitted }))) : null),
2049
+ React.createElement("section", { className: "section section__sub", "aria-label": t("live.action.sectionTitle") },
2050
+ React.createElement("div", { className: "row-baseline section-label" },
2051
+ React.createElement(Embassy.MonoLabel, null, t("live.action.sectionTitle"))),
2052
+ React.createElement("p", { className: "section-teaching" }, t("live.action.scope")),
2053
+ React.createElement("div", { className: "action-panel" },
2054
+ React.createElement(ActionControl, { action: { action: "refresh_dashboard" }, consequence: t("app.routes.refreshCmd"), enabled: props.actionsEnabled, onAction: props.onAction })))),
2055
+ React.createElement("section", { className: "section", "aria-label": t("app.routes.claudeSessions") },
2056
+ React.createElement("div", { className: "row-baseline row-baseline--split section-label" },
2057
+ React.createElement(Embassy.MonoLabel, null, t("app.routes.claudeSessions")),
2058
+ data.peersOmitted > 0 ? (React.createElement(LowerBoundBadge, { count: data.peers.length })) : null),
2059
+ data.peers.length === 0 ? (React.createElement("div", { className: "empty-state" },
2060
+ React.createElement("p", { className: "empty-state__text" }, t("app.routes.noPeers")),
2061
+ React.createElement(Embassy.CopyCmd, { cmd: SELECT_CLAUDE_COMMAND }))) : (React.createElement("div", null, data.peers.map((peer, index) => {
2062
+ const identity = rowIdentity(peer.alias, peer.host);
2063
+ return (React.createElement(PeerAccordionRow, { key: `${identity}|${index}`, peer: peer, open: openPeer === identity, pairs: data.pairs, codexRoutes: data.codexRoutes, actionsEnabled: props.actionsEnabled, onAction: props.onAction, onToggle: () => {
2064
+ setOpenPeer(openPeer === identity ? undefined : identity);
2065
+ } }));
2066
+ }))),
2067
+ React.createElement(Embassy.Rule, null)),
2068
+ React.createElement("section", { className: "section", "aria-label": t("app.routes.codexRoutes") },
2069
+ React.createElement("div", { className: "row-baseline row-baseline--split section-label" },
2070
+ React.createElement(Embassy.MonoLabel, null, t("app.routes.codexRoutes")),
2071
+ data.routesOmitted > 0 ? (React.createElement(LowerBoundBadge, { count: data.codexRoutes.length })) : null),
2072
+ data.codexRoutes.length === 0 ? (React.createElement("div", { className: "empty-state" },
2073
+ React.createElement("p", { className: "empty-state__text" }, t("app.routes.noCodex")))) : (React.createElement("div", null, data.codexRoutes.map((view, index) => {
2074
+ const identity = rowIdentity(view.route.alias, view.route.host);
2075
+ return (React.createElement(CodexAccordionRow, { key: `${identity}|${index}`, view: view, open: openRoute === identity, onToggle: () => {
2076
+ setOpenRoute(openRoute === identity ? undefined : identity);
2077
+ } }));
2078
+ }))),
2079
+ React.createElement(Embassy.Rule, null),
2080
+ React.createElement("div", { className: "stack routes-register" },
2081
+ React.createElement("p", { className: "text-xs text-body-muted" }, t("app.routes.registerHint")),
2082
+ React.createElement(Embassy.CopyCmd, { cmd: REGISTER_CODEX_COMMAND }))),
2083
+ React.createElement("section", { className: "section", "aria-label": t("app.routes.successions") },
2084
+ React.createElement(Embassy.MonoLabel, { className: "section-label" }, t("app.routes.successions")),
2085
+ data.successions.length === 0 ? (React.createElement("div", { className: "empty-state" },
2086
+ React.createElement("p", { className: "empty-state__text" }, t("app.routes.successions.empty")),
2087
+ React.createElement("p", { className: "succession-note" }, t("app.routes.successions.note")),
2088
+ React.createElement(Embassy.CopyCmd, { cmd: SUCCEEDS_COMMAND }))) : (React.createElement("div", { className: "stack-lg" }, data.successions.map((view, index) => (React.createElement(SuccessionCard, { key: `${view.guidanceKey}|${view.item.alias ?? ""}|${index}`, view: view }))))))));
2089
+ }
2090
+ Embassy.RoutesTab = RoutesTab;
2091
+ })(Embassy || (Embassy = {}));
2092
+ var Embassy;
2093
+ (function (Embassy) {
2094
+ const ACTIVITY_KINDS = [
2095
+ "delivery",
2096
+ "operation",
2097
+ "alert",
2098
+ ];
2099
+ const ACTIVITY_KIND_LABEL_KEYS = {
2100
+ delivery: "app.activity.kinds.delivery",
2101
+ operation: "app.activity.kinds.operation",
2102
+ alert: "app.activity.kinds.alert",
2103
+ };
2104
+ const OPERATION_COPY_KEYS = {
2105
+ discovery_refreshed: "app.activity.operation.discoveryRefreshed",
2106
+ claude_selected: "app.activity.operation.claudeSelected",
2107
+ claude_unselected: "app.activity.operation.claudeUnselected",
2108
+ codex_registered: "app.activity.operation.codexRegistered",
2109
+ codex_succeeded: "app.activity.operation.codexSucceeded",
2110
+ codex_unregistered: "app.activity.operation.codexUnregistered",
2111
+ routes_paired: "app.activity.operation.routesPaired",
2112
+ routes_unpaired: "app.activity.operation.routesUnpaired",
2113
+ watch_ended: "app.activity.operation.watchEnded",
2114
+ };
2115
+ const ACTIVITY_EMPTY_COMMAND = "embassy send-to-codex --from <alias> --to <alias>";
2116
+ const activityCountFormatters = new Map();
2117
+ function formatCount(count, locale) {
2118
+ const existing = activityCountFormatters.get(locale);
2119
+ if (existing !== undefined)
2120
+ return existing.format(count);
2121
+ const formatter = new Intl.NumberFormat(locale);
2122
+ activityCountFormatters.set(locale, formatter);
2123
+ return formatter.format(count);
2124
+ }
2125
+ function countByKind(rows) {
2126
+ let delivery = 0;
2127
+ let operation = 0;
2128
+ let alert = 0;
2129
+ for (const row of rows) {
2130
+ if (row.kind === "delivery")
2131
+ delivery += 1;
2132
+ else if (row.kind === "operation")
2133
+ operation += 1;
2134
+ else
2135
+ alert += 1;
2136
+ }
2137
+ return { all: rows.length, delivery, operation, alert };
2138
+ }
2139
+ function kindIsLowerBound(kind, omissions) {
2140
+ if (kind === "delivery") {
2141
+ return omissions.messageGroups > 0 || omissions.upstreamMessageEvents > 0;
2142
+ }
2143
+ if (kind === "operation") {
2144
+ return omissions.activityEvents > 0 || omissions.upstreamActivityEvents > 0;
2145
+ }
2146
+ return omissions.attentionItems > 0 || omissions.upstreamAlerts > 0;
2147
+ }
2148
+ function filterIsLowerBound(filter, omissions) {
2149
+ return filter === "all"
2150
+ ? ACTIVITY_KINDS.some((kind) => kindIsLowerBound(kind, omissions))
2151
+ : kindIsLowerBound(filter, omissions);
2152
+ }
2153
+ function activityRowIdentity(row) {
2154
+ if (row.kind === "delivery") {
2155
+ return `delivery|${Embassy.deliveryGroupKey(row.group)}`;
2156
+ }
2157
+ if (row.kind === "operation") {
2158
+ return `operation|${row.event.sequence}|${row.event.action}`;
2159
+ }
2160
+ const item = row.item;
2161
+ return `alert|${row.timestamp}|${item.guidance}|${item.alias ?? ""}|${item.host ?? ""}|${item.code ?? ""}`;
2162
+ }
2163
+ function activityRowKeys(rows) {
2164
+ const seen = new Map();
2165
+ return rows.map((row) => {
2166
+ const identity = activityRowIdentity(row);
2167
+ const occurrence = seen.get(identity) ?? 0;
2168
+ seen.set(identity, occurrence + 1);
2169
+ return occurrence === 0 ? identity : `${identity}#${occurrence}`;
2170
+ });
2171
+ }
2172
+ function alertScope(item, t) {
2173
+ const parts = [];
2174
+ if (item.provider !== undefined) {
2175
+ parts.push(t(item.provider === "claude" ? "provider.claude" : "provider.codex"));
2176
+ }
2177
+ if (item.alias !== undefined)
2178
+ parts.push(item.alias);
2179
+ if (item.host !== undefined)
2180
+ parts.push(item.host);
2181
+ if (item.code !== undefined)
2182
+ parts.push(item.code);
2183
+ return parts.join(" · ");
2184
+ }
2185
+ function DeliveryEntryText(props) {
2186
+ const group = props.group;
2187
+ return (React.createElement("div", { className: "activity-text chip-row" },
2188
+ React.createElement("span", { className: "mono" },
2189
+ group.sourceAlias,
2190
+ " \u2192 ",
2191
+ group.targetAlias),
2192
+ React.createElement(Embassy.StateChip, { small: true, state: group.state, direction: group.direction, safeErrorCode: group.safeErrorCode }),
2193
+ group.steer === true ? (React.createElement("span", { className: "mono text-muted" }, "STEER")) : null,
2194
+ group.messageIdSuffix === undefined ? null : (React.createElement("span", { className: "mono text-muted" }, group.messageIdSuffix))));
2195
+ }
2196
+ function AlertEntryText(props) {
2197
+ const t = Embassy.useT();
2198
+ const scope = alertScope(props.item, t);
2199
+ return (React.createElement("div", { className: "activity-text stack-sm" },
2200
+ React.createElement("div", { className: "chip-row" },
2201
+ React.createElement(Embassy.StateChip, { small: true, domain: "severity", state: props.item.severity }),
2202
+ React.createElement("span", null, t(`guidance.${props.guidanceKey}.title`))),
2203
+ scope === "" ? null : (React.createElement("span", { className: "attention-item__scope" },
2204
+ t("attention.scope"),
2205
+ ": ",
2206
+ scope))));
2207
+ }
2208
+ function OperationEntryText(props) {
2209
+ const t = Embassy.useT();
2210
+ const event = props.event;
2211
+ return (React.createElement("div", { className: "activity-text stack-sm" },
2212
+ React.createElement("div", { className: "chip-row" },
2213
+ React.createElement("span", null, t(OPERATION_COPY_KEYS[event.action])),
2214
+ React.createElement("span", { className: "mono text-muted" }, t(event.outcome === "accepted"
2215
+ ? "app.activity.operation.accepted"
2216
+ : "app.activity.operation.rejected"))),
2217
+ event.aliases.length === 0 && event.safeErrorCode === undefined ? null : (React.createElement("span", { className: "attention-item__scope" }, [...event.aliases, event.safeErrorCode]
2218
+ .filter((part) => part !== undefined)
2219
+ .join(" · ")))));
2220
+ }
2221
+ function ActivityEntry(props) {
2222
+ const t = Embassy.useT();
2223
+ const row = props.row;
2224
+ return (React.createElement("div", { className: "activity-row", role: "listitem" },
2225
+ row.timestamp === undefined ? (React.createElement("span", { className: "time-ago text-muted", title: t("transit.unavailable") },
2226
+ "\u2014",
2227
+ React.createElement("span", { className: "sr-only" }, t("transit.unavailable")))) : (React.createElement(Embassy.TimeAgo, { iso: row.timestamp })),
2228
+ React.createElement("span", { className: "activity-kind" }, t(ACTIVITY_KIND_LABEL_KEYS[row.kind])),
2229
+ 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 }))));
2230
+ }
2231
+ function ActivityTab(props) {
2232
+ const t = Embassy.useT();
2233
+ const [locale] = Embassy.useLocale();
2234
+ const headingId = React.useId();
2235
+ const [kindFilter, setKindFilter] = React.useState("all");
2236
+ const rows = props.rows;
2237
+ const counts = React.useMemo(() => countByKind(rows), [rows]);
2238
+ const shown = React.useMemo(() => kindFilter === "all"
2239
+ ? rows
2240
+ : rows.filter((row) => row.kind === kindFilter), [rows, kindFilter]);
2241
+ const keys = React.useMemo(() => activityRowKeys(shown), [shown]);
2242
+ const showAll = React.useCallback(() => {
2243
+ setKindFilter("all");
2244
+ }, []);
2245
+ const toggleKind = React.useCallback((kind) => {
2246
+ setKindFilter((current) => (current === kind ? "all" : kind));
2247
+ }, []);
2248
+ const isLowerBound = filterIsLowerBound(kindFilter, props.omissions);
2249
+ return (React.createElement("section", { className: "section tab-panel--narrow", "aria-labelledby": headingId },
2250
+ React.createElement("h2", { id: headingId, className: "mono-label section-label" }, t("app.activity.title")),
2251
+ React.createElement("div", { className: "filters" },
2252
+ React.createElement("div", { className: "filter-group" },
2253
+ React.createElement("div", { className: "pill-row" },
2254
+ React.createElement(Embassy.FilterPill, { active: kindFilter === "all", onClick: showAll },
2255
+ t("app.activity.kinds.all"),
2256
+ " ",
2257
+ React.createElement("span", { className: "pill__count" }, formatCount(counts.all, locale))),
2258
+ ACTIVITY_KINDS.map((kind) => (React.createElement(Embassy.FilterPill, { key: kind, active: kindFilter === kind, onClick: () => {
2259
+ toggleKind(kind);
2260
+ } },
2261
+ t(ACTIVITY_KIND_LABEL_KEYS[kind]),
2262
+ " ",
2263
+ React.createElement("span", { className: "pill__count" }, formatCount(counts[kind], locale)))))),
2264
+ isLowerBound ? (React.createElement("div", { className: "chip-row" },
2265
+ React.createElement("span", { className: "lower-bound" }, t("app.lowerBound", {
2266
+ count: formatCount(shown.length, locale),
2267
+ })))) : null)),
2268
+ React.createElement("div", { className: "stack" },
2269
+ shown.length === 0 ? (React.createElement("div", { className: "empty-state empty-state--activity" },
2270
+ React.createElement("p", { className: "empty-state__text" }, t("app.activity.empty")),
2271
+ React.createElement(Embassy.CopyCmd, { cmd: ACTIVITY_EMPTY_COMMAND }))) : (React.createElement("div", null,
2272
+ React.createElement("div", { className: "activity-list", role: "list" }, shown.map((row, index) => (React.createElement(ActivityEntry, { key: keys[index] ?? index, row: row })))),
2273
+ React.createElement(Embassy.Rule, null))),
2274
+ React.createElement("p", { className: "footnote" }, t("app.activity.limited")))));
2275
+ }
2276
+ Embassy.ActivityTab = ActivityTab;
2277
+ })(Embassy || (Embassy = {}));
2278
+ var Embassy;
2279
+ (function (Embassy) {
2280
+ const DIAGNOSTICS_ABSENT_FIELD = "—";
2281
+ const DIAGNOSTICS_CONNECTOR_COLUMNS = 7;
2282
+ const diagnosticsNumberFormatters = new Map();
2283
+ function diagnosticsNumberFormatter(locale) {
2284
+ const existing = diagnosticsNumberFormatters.get(locale);
2285
+ if (existing !== undefined)
2286
+ return existing;
2287
+ const formatter = new Intl.NumberFormat(locale);
2288
+ diagnosticsNumberFormatters.set(locale, formatter);
2289
+ return formatter;
2290
+ }
2291
+ function formatDiagnosticsCount(value, locale) {
2292
+ return diagnosticsNumberFormatter(locale).format(value);
2293
+ }
2294
+ function formatDiagnosticsBytes(value, locale) {
2295
+ if (value < 1_024) {
2296
+ return `${formatDiagnosticsCount(value, locale)} B`;
2297
+ }
2298
+ if (value < 1_048_576) {
2299
+ const kibibytes = value / 1_024;
2300
+ const rounded = kibibytes < 10 ? Math.round(kibibytes * 10) / 10 : Math.round(kibibytes);
2301
+ return `${formatDiagnosticsCount(rounded, locale)} KiB`;
2302
+ }
2303
+ const mebibytes = value / 1_048_576;
2304
+ const rounded = mebibytes < 10 ? Math.round(mebibytes * 10) / 10 : Math.round(mebibytes);
2305
+ return `${formatDiagnosticsCount(rounded, locale)} MiB`;
2306
+ }
2307
+ function DiagnosticsLowerBound(props) {
2308
+ const t = Embassy.useT();
2309
+ const [locale] = Embassy.useLocale();
2310
+ return (React.createElement("span", { className: "lower-bound" }, t("app.lowerBound", {
2311
+ count: formatDiagnosticsCount(props.count, locale),
2312
+ })));
2313
+ }
2314
+ function DiagnosticsConnectorTable(props) {
2315
+ const t = Embassy.useT();
2316
+ const { connectors } = props;
2317
+ return (React.createElement("div", { className: "table-wrap" },
2318
+ React.createElement("table", { className: "data-table" },
2319
+ React.createElement("caption", { className: "sr-only" }, t("app.diag.versions.caption")),
2320
+ React.createElement("thead", null,
2321
+ React.createElement("tr", null,
2322
+ React.createElement("th", { scope: "col" }, t("app.diag.col.provider")),
2323
+ React.createElement("th", { scope: "col" }, t("app.diag.col.host")),
2324
+ React.createElement("th", { scope: "col" }, t("app.diag.col.protocol")),
2325
+ React.createElement("th", { scope: "col" }, t("app.diag.col.version")),
2326
+ React.createElement("th", { scope: "col" }, t("diagnostics.health")),
2327
+ React.createElement("th", { scope: "col" }, t("app.diag.col.compat")),
2328
+ React.createElement("th", { scope: "col" }, t("column.issue")))),
2329
+ React.createElement("tbody", null, connectors.length === 0 ? (React.createElement("tr", null,
2330
+ React.createElement("td", { colSpan: DIAGNOSTICS_CONNECTOR_COLUMNS }, t("diagnostics.connectors.empty")))) : (connectors.map((connector, index) => (React.createElement("tr", { key: `${connector.provider}|${connector.host}|${index}` },
2331
+ React.createElement("th", { scope: "row" }, connector.provider === "claude"
2332
+ ? t("provider.claude")
2333
+ : t("provider.codex")),
2334
+ React.createElement("td", { className: "cell-mono" }, connector.host),
2335
+ React.createElement("td", { className: "cell-mono" }, connector.protocol ?? DIAGNOSTICS_ABSENT_FIELD),
2336
+ React.createElement("td", { className: "cell-mono" }, connector.protocolVersion ?? DIAGNOSTICS_ABSENT_FIELD),
2337
+ React.createElement("td", null,
2338
+ React.createElement(Embassy.StateChip, { domain: "health", state: connector.health, small: true })),
2339
+ React.createElement("td", null,
2340
+ React.createElement(Embassy.StateChip, { domain: "compatibility", state: connector.compatibility, small: true })),
2341
+ React.createElement("td", { className: "cell-mono" }, connector.safeErrorCode ?? DIAGNOSTICS_ABSENT_FIELD)))))))));
2342
+ }
2343
+ function DiagnosticsCompatibilityTable(props) {
2344
+ const t = Embassy.useT();
2345
+ return (React.createElement("div", { className: "table-wrap" },
2346
+ React.createElement("table", { className: "data-table" },
2347
+ React.createElement("caption", { className: "sr-only" }, t("diagnostics.compatibilityChecks.caption")),
2348
+ React.createElement("thead", null,
2349
+ React.createElement("tr", null,
2350
+ React.createElement("th", { scope: "col" }, t("app.diag.col.provider")),
2351
+ React.createElement("th", { scope: "col" }, t("diagnostics.version")),
2352
+ React.createElement("th", { scope: "col" }, t("diagnostics.tier")),
2353
+ React.createElement("th", { scope: "col" }, t("diagnostics.checkedAt")),
2354
+ React.createElement("th", { scope: "col" }, t("diagnostics.certification")),
2355
+ React.createElement("th", { scope: "col" }, t("diagnostics.certifiedAt")),
2356
+ React.createElement("th", { scope: "col" }, t("diagnostics.failedProbe")),
2357
+ React.createElement("th", { scope: "col" }, t("column.issue")))),
2358
+ React.createElement("tbody", null, props.checks.length === 0 ? (React.createElement("tr", null,
2359
+ React.createElement("td", { colSpan: 8 }, t("diagnostics.compatibilityChecks.empty")))) : (props.checks.map((check) => (React.createElement("tr", { key: check.surface },
2360
+ React.createElement("th", { scope: "row" }, check.surface === "claude"
2361
+ ? t("provider.claude")
2362
+ : t("provider.codex")),
2363
+ React.createElement("td", { className: "cell-mono" }, check.version),
2364
+ React.createElement("td", null, t(`compatibilityTier.${check.tier}`)),
2365
+ React.createElement("td", null,
2366
+ React.createElement(Embassy.TimeAgo, { iso: check.checkedAt })),
2367
+ React.createElement("td", { className: "cell-mono" }, check.certificationOutcome === undefined
2368
+ ? DIAGNOSTICS_ABSENT_FIELD
2369
+ : `${check.certificationOutcome} / ${check.certificationDepth}`),
2370
+ React.createElement("td", null, check.certifiedAt === undefined
2371
+ ? DIAGNOSTICS_ABSENT_FIELD
2372
+ : React.createElement(Embassy.TimeAgo, { iso: check.certifiedAt })),
2373
+ React.createElement("td", { className: "cell-mono" }, check.failedProbe ?? DIAGNOSTICS_ABSENT_FIELD),
2374
+ React.createElement("td", { className: "cell-mono" }, check.certificationSafeErrorCode ??
2375
+ check.safeErrorCode ??
2376
+ DIAGNOSTICS_ABSENT_FIELD)))))))));
2377
+ }
2378
+ function DiagnosticsCounterTable(props) {
2379
+ return (React.createElement("div", { className: "table-wrap counters-list" },
2380
+ React.createElement("table", { className: "data-table data-table--counters" },
2381
+ React.createElement("caption", { className: "table-caption" }, props.caption),
2382
+ React.createElement("tbody", null, props.rows.map((row) => (React.createElement("tr", { key: row.id },
2383
+ React.createElement("th", { scope: "row" }, row.label),
2384
+ React.createElement("td", { className: "cell-mono" }, row.value)))))),
2385
+ React.createElement(Embassy.Rule, null)));
2386
+ }
2387
+ function DiagnosticsOmissionTable(props) {
2388
+ return (React.createElement("div", { className: "table-wrap" },
2389
+ React.createElement("table", { className: "data-table" },
2390
+ React.createElement("caption", { className: "table-caption" }, props.caption),
2391
+ React.createElement("tbody", null, props.rows.map((row) => (React.createElement("tr", { key: row.field },
2392
+ React.createElement("th", { scope: "row", className: "cell-mono" }, row.field),
2393
+ React.createElement("td", null, row.text)))))),
2394
+ React.createElement(Embassy.Rule, null)));
2395
+ }
2396
+ function DiagnosticsDisclosure(props) {
2397
+ const t = Embassy.useT();
2398
+ const [open, setOpen] = React.useState(false);
2399
+ return (React.createElement("details", { className: "details-block", onToggle: (event) => {
2400
+ setOpen(event.currentTarget.open);
2401
+ } },
2402
+ React.createElement("summary", null,
2403
+ props.label,
2404
+ " \u00B7 ",
2405
+ open ? t("app.hide") : t("app.show")),
2406
+ props.children));
2407
+ }
2408
+ function DiagnosticsSetting(props) {
2409
+ const { setting } = props;
2410
+ return (React.createElement("div", { className: "setting-row" },
2411
+ React.createElement("div", { className: "row-baseline" },
2412
+ React.createElement("span", { className: setting.nameIsToken
2413
+ ? "detail-value detail-value--mono"
2414
+ : "detail-value" }, setting.name),
2415
+ React.createElement("span", { className: "mono-label" }, setting.where)),
2416
+ React.createElement("div", { className: "env-name" }, setting.envName ?? DIAGNOSTICS_ABSENT_FIELD),
2417
+ setting.note === undefined ? null : (React.createElement("p", { className: "footnote" }, setting.note))));
2418
+ }
2419
+ function DiagnosticsTab(props) {
2420
+ const t = Embassy.useT();
2421
+ const [locale] = Embassy.useLocale();
2422
+ const { data } = props;
2423
+ const versionsHeadingId = React.useId();
2424
+ const limitsHeadingId = React.useId();
2425
+ const deadlineHeadingId = React.useId();
2426
+ const queueHeadingId = React.useId();
2427
+ const bytesHeadingId = React.useId();
2428
+ const settingsHeadingId = React.useId();
2429
+ const deadlineIsWarn = data.expiredCount > 0;
2430
+ const deadlineBucketKeys = {
2431
+ under_1m: "app.diag.deadline.bucket.under1m",
2432
+ "1m_to_5m": "app.diag.deadline.bucket.1to5m",
2433
+ "5m_to_15m": "app.diag.deadline.bucket.5to15m",
2434
+ "15m_to_60m": "app.diag.deadline.bucket.15to60m",
2435
+ over_60m: "app.diag.deadline.bucket.over60m",
2436
+ };
2437
+ const accounting = data.accounting;
2438
+ const accountingRows = [
2439
+ {
2440
+ id: "accepted",
2441
+ label: t("diagnostics.accepted"),
2442
+ value: formatDiagnosticsCount(accounting.accepted, locale),
2443
+ },
2444
+ {
2445
+ id: "duplicates",
2446
+ label: t("diagnostics.duplicates"),
2447
+ value: formatDiagnosticsCount(accounting.duplicates, locale),
2448
+ },
2449
+ {
2450
+ id: "delivered",
2451
+ label: t("diagnostics.delivered"),
2452
+ value: formatDiagnosticsCount(accounting.delivered, locale),
2453
+ },
2454
+ {
2455
+ id: "unconfirmed",
2456
+ label: t("diagnostics.unconfirmed"),
2457
+ value: formatDiagnosticsCount(accounting.unconfirmed, locale),
2458
+ },
2459
+ {
2460
+ id: "ambiguous",
2461
+ label: t("diagnostics.ambiguous"),
2462
+ value: formatDiagnosticsCount(accounting.ambiguous, locale),
2463
+ },
2464
+ {
2465
+ id: "failed",
2466
+ label: t("diagnostics.failed"),
2467
+ value: formatDiagnosticsCount(accounting.failed, locale),
2468
+ },
2469
+ {
2470
+ id: "expired",
2471
+ label: t("diagnostics.expired"),
2472
+ value: formatDiagnosticsCount(accounting.expired, locale),
2473
+ },
2474
+ {
2475
+ id: "cancelled",
2476
+ label: t("diagnostics.cancelled"),
2477
+ value: formatDiagnosticsCount(accounting.cancelled, locale),
2478
+ },
2479
+ {
2480
+ id: "abandoned",
2481
+ label: t("diagnostics.abandoned"),
2482
+ value: formatDiagnosticsCount(accounting.abandoned, locale),
2483
+ },
2484
+ {
2485
+ id: "rejected",
2486
+ label: t("diagnostics.rejected"),
2487
+ value: formatDiagnosticsCount(accounting.rejected, locale),
2488
+ },
2489
+ {
2490
+ id: "bytesAccepted",
2491
+ label: t("diagnostics.bytesAccepted"),
2492
+ value: formatDiagnosticsBytes(accounting.bytesAccepted, locale),
2493
+ },
2494
+ {
2495
+ id: "queuedBytes",
2496
+ label: t("diagnostics.queuedBytes"),
2497
+ value: formatDiagnosticsBytes(accounting.queuedBytes, locale),
2498
+ },
2499
+ ];
2500
+ const omissions = data.omissions;
2501
+ const omissionRows = [
2502
+ {
2503
+ field: "connectors",
2504
+ text: t("diagnostics.omissions.connectors", {
2505
+ count: formatDiagnosticsCount(omissions.connectors, locale),
2506
+ }),
2507
+ },
2508
+ {
2509
+ field: "availablePeers",
2510
+ text: t("diagnostics.omissions.peers", {
2511
+ count: formatDiagnosticsCount(omissions.availablePeers, locale),
2512
+ }),
2513
+ },
2514
+ {
2515
+ field: "routes",
2516
+ text: t("diagnostics.omissions.routes", {
2517
+ count: formatDiagnosticsCount(omissions.routes, locale),
2518
+ }),
2519
+ },
2520
+ {
2521
+ field: "upstreamMessageEvents",
2522
+ text: t("diagnostics.omissions.upstreamMessageEvents", {
2523
+ count: formatDiagnosticsCount(omissions.upstreamMessageEvents, locale),
2524
+ }),
2525
+ },
2526
+ {
2527
+ field: "messageGroups",
2528
+ text: t("diagnostics.omissions.messageGroups", {
2529
+ count: formatDiagnosticsCount(omissions.messageGroups, locale),
2530
+ }),
2531
+ },
2532
+ {
2533
+ field: "messageEvents",
2534
+ text: t("diagnostics.omissions.messageEvents", {
2535
+ count: formatDiagnosticsCount(omissions.messageEvents, locale),
2536
+ }),
2537
+ },
2538
+ {
2539
+ field: "upstreamAlerts",
2540
+ text: t("diagnostics.omissions.upstreamAlerts", {
2541
+ count: formatDiagnosticsCount(omissions.upstreamAlerts, locale),
2542
+ }),
2543
+ },
2544
+ {
2545
+ field: "attentionItems",
2546
+ text: t("diagnostics.omissions.attentionItems", {
2547
+ count: formatDiagnosticsCount(omissions.attentionItems, locale),
2548
+ }),
2549
+ },
2550
+ ];
2551
+ const nothingOmitted = omissions.connectors === 0 &&
2552
+ omissions.availablePeers === 0 &&
2553
+ omissions.routes === 0 &&
2554
+ omissions.upstreamMessageEvents === 0 &&
2555
+ omissions.messageGroups === 0 &&
2556
+ omissions.messageEvents === 0 &&
2557
+ omissions.upstreamAlerts === 0 &&
2558
+ omissions.attentionItems === 0;
2559
+ const settings = [
2560
+ {
2561
+ id: "deadline",
2562
+ name: t("app.diag.deadline.title"),
2563
+ nameIsToken: false,
2564
+ envName: "EMBASSY_MESSAGE_DEADLINE_MS",
2565
+ where: t("app.tab.diagnostics"),
2566
+ note: undefined,
2567
+ },
2568
+ {
2569
+ id: "queueDepth",
2570
+ name: t("app.diag.queue.title"),
2571
+ nameIsToken: false,
2572
+ envName: "EMBASSY_MAX_QUEUE_MESSAGES",
2573
+ where: t("app.tab.overview"),
2574
+ note: undefined,
2575
+ },
2576
+ {
2577
+ id: "deliveryNotices",
2578
+ name: t("app.diag.deliveryNotices.title"),
2579
+ nameIsToken: false,
2580
+ envName: "EMBASSY_DELIVERY_NOTICES",
2581
+ where: t("app.tab.diagnostics"),
2582
+ note: t("app.diag.deliveryNotices.note"),
2583
+ },
2584
+ {
2585
+ id: "steering",
2586
+ name: "steering",
2587
+ nameIsToken: true,
2588
+ envName: undefined,
2589
+ where: t("app.tab.diagnostics"),
2590
+ note: undefined,
2591
+ },
2592
+ {
2593
+ id: "inboundMode",
2594
+ name: "inbound mode",
2595
+ nameIsToken: true,
2596
+ envName: undefined,
2597
+ where: t("app.tab.routes"),
2598
+ note: t("app.routes.detail.absent"),
2599
+ },
2600
+ ];
2601
+ return (React.createElement("div", { className: "tab-panel tab-panel--narrow" },
2602
+ React.createElement("section", { className: "section", "aria-labelledby": settingsHeadingId },
2603
+ React.createElement("h2", { className: "mono-label section-label", id: settingsHeadingId }, t("app.diag.editable.title")),
2604
+ React.createElement("div", { className: "stack-lg" },
2605
+ React.createElement("div", { className: "grid-2" },
2606
+ React.createElement("section", { className: deadlineIsWarn ? "card card--warn" : "card", "aria-labelledby": deadlineHeadingId },
2607
+ React.createElement("div", { className: "card__head" },
2608
+ React.createElement("h3", { className: "card__title", id: deadlineHeadingId }, t("app.diag.deadline.title")),
2609
+ deadlineIsWarn ? (React.createElement(Embassy.StateChip, { domain: "severity", state: "warning", small: true })) : null),
2610
+ React.createElement("p", { className: "card__body" }, t("app.diag.deadline.body", {
2611
+ count: formatDiagnosticsCount(data.expiredCount, locale),
2612
+ })),
2613
+ data.deadlinePressure === undefined ? null : (React.createElement("div", { className: "stack-sm" },
2614
+ React.createElement("p", { className: "footnote" }, t("app.diag.deadline.retained", {
2615
+ terminal: formatDiagnosticsCount(data.deadlinePressure.terminalEvents, locale),
2616
+ expired: formatDiagnosticsCount(data.deadlinePressure.expiredEvents, locale),
2617
+ deadline: Embassy.fmtAge(data.deadlinePressure.configuredDeadlineMs),
2618
+ })),
2619
+ React.createElement("dl", { className: "detail-list" }, data.deadlinePressure.buckets.map((bucket) => (React.createElement(React.Fragment, { key: bucket.bucket },
2620
+ React.createElement("dt", null, t(deadlineBucketKeys[bucket.bucket])),
2621
+ React.createElement("dd", null,
2622
+ formatDiagnosticsCount(bucket.settled, locale),
2623
+ " \u00B7 ",
2624
+ formatDiagnosticsCount(bucket.expired, locale),
2625
+ " ",
2626
+ t("delivery.expired")))))))),
2627
+ React.createElement("p", { className: "env-name" }, "EMBASSY_MESSAGE_DEADLINE_MS"),
2628
+ React.createElement(Embassy.CopyCmd, { cmd: "EMBASSY_MESSAGE_DEADLINE_MS=<ms> embassy serve" })),
2629
+ React.createElement(Embassy.AbsentFeature, { title: t("app.diag.steering.title"), body: t("app.diag.steering.absent") })),
2630
+ React.createElement("div", { className: "setting-list" }, settings.map((setting) => (React.createElement(DiagnosticsSetting, { key: setting.id, setting: setting })))),
2631
+ React.createElement("p", { className: "footnote" }, t("app.diag.editable.note")))),
2632
+ React.createElement("section", { className: "section", "aria-labelledby": versionsHeadingId },
2633
+ React.createElement("div", { className: "row-baseline section-label" },
2634
+ React.createElement("h2", { className: "mono-label", id: versionsHeadingId }, t("app.diag.versions")),
2635
+ data.connectorsOmitted > 0 ? (React.createElement(DiagnosticsLowerBound, { count: data.connectors.length })) : null),
2636
+ React.createElement("div", { className: "stack" },
2637
+ React.createElement(DiagnosticsConnectorTable, { connectors: data.connectors }),
2638
+ React.createElement("p", { className: "footnote" }, t("app.diag.versions.rangeAbsent")))),
2639
+ React.createElement("div", { className: "grid-2" },
2640
+ React.createElement("section", { className: "card", "aria-labelledby": "compatibility-attestation-title" },
2641
+ React.createElement("div", { className: "card__head" },
2642
+ React.createElement("h3", { className: "card__title", id: "compatibility-attestation-title" }, t("app.diag.attestation.title"))),
2643
+ React.createElement(DiagnosticsCompatibilityTable, { checks: data.compatibilityChecks }),
2644
+ React.createElement(Embassy.CopyCmd, { cmd: "embassy compat-check" })),
2645
+ React.createElement(Embassy.AbsentFeature, { title: t("app.diag.lease.title"), body: t("app.diag.lease.absent"), cmd: "embassy status" })),
2646
+ React.createElement("section", { className: "section", "aria-labelledby": limitsHeadingId },
2647
+ React.createElement("h2", { className: "mono-label section-label", id: limitsHeadingId }, t("app.diag.limits")),
2648
+ React.createElement("div", { className: "stack" },
2649
+ React.createElement("section", { className: "card", "aria-labelledby": queueHeadingId },
2650
+ React.createElement("div", { className: "card__head" },
2651
+ React.createElement("h3", { className: "card__title", id: queueHeadingId }, t("app.diag.queue.title")),
2652
+ data.queueCountIsLowerBound ? (React.createElement(DiagnosticsLowerBound, { count: data.queuedMessages })) : null),
2653
+ React.createElement("p", { className: "card__body" }, t("app.diag.queue.body", {
2654
+ count: formatDiagnosticsCount(data.queuedMessages, locale),
2655
+ })),
2656
+ React.createElement("p", { className: "env-name" }, "EMBASSY_MAX_QUEUE_MESSAGES"),
2657
+ React.createElement(Embassy.CopyCmd, { cmd: "EMBASSY_MAX_QUEUE_MESSAGES=<n> embassy serve" })),
2658
+ React.createElement("section", { className: "card", "aria-labelledby": bytesHeadingId },
2659
+ React.createElement("div", { className: "card__head" },
2660
+ React.createElement("h3", { className: "card__title", id: bytesHeadingId }, t("app.diag.bytes.title"))),
2661
+ React.createElement("p", { className: "card__body" }, t("app.diag.bytes.body", {
2662
+ queued: formatDiagnosticsBytes(accounting.queuedBytes, locale),
2663
+ accepted: formatDiagnosticsBytes(accounting.bytesAccepted, locale),
2664
+ })),
2665
+ React.createElement("p", { className: "env-name" }, "EMBASSY_MAX_MESSAGE_BYTES"),
2666
+ React.createElement(Embassy.CopyCmd, { cmd: "EMBASSY_MAX_MESSAGE_BYTES=<n> embassy serve" })),
2667
+ React.createElement("p", { className: "footnote" }, t("app.diag.limits.hint")))),
2668
+ React.createElement("section", { className: "section" },
2669
+ React.createElement(DiagnosticsDisclosure, { label: t("app.diag.counters") },
2670
+ React.createElement("div", { className: "details-block__content" },
2671
+ React.createElement(DiagnosticsCounterTable, { caption: t("app.diag.counters.caption"), rows: accountingRows }))),
2672
+ React.createElement(DiagnosticsDisclosure, { label: t("app.diag.omissions") },
2673
+ React.createElement("div", { className: "details-block__content stack" },
2674
+ React.createElement(DiagnosticsOmissionTable, { caption: t("app.diag.omissions.caption"), rows: omissionRows }),
2675
+ nothingOmitted ? (React.createElement("p", { className: "footnote" }, t("diagnostics.omissions.none"))) : null)))));
2676
+ }
2677
+ Embassy.DiagnosticsTab = DiagnosticsTab;
2678
+ })(Embassy || (Embassy = {}));
2679
+ var Embassy;
2680
+ (function (Embassy) {
2681
+ const PREFS_STORAGE_KEY = "embassy-live-prefs";
2682
+ const STALE_NOTICE_MS = 35_000;
2683
+ const TAB_KEYS = [
2684
+ "overview",
2685
+ "deliveries",
2686
+ "routes",
2687
+ "activity",
2688
+ "diagnostics",
2689
+ ];
2690
+ function isTabKey(value) {
2691
+ return (typeof value === "string" &&
2692
+ TAB_KEYS.includes(value));
2693
+ }
2694
+ function loadPrefs(fallbackLocale) {
2695
+ try {
2696
+ const raw = window.localStorage.getItem(PREFS_STORAGE_KEY);
2697
+ if (raw !== null) {
2698
+ const parsed = JSON.parse(raw);
2699
+ if (typeof parsed === "object" && parsed !== null) {
2700
+ const record = parsed;
2701
+ const locale = record["locale"];
2702
+ const tab = record["tab"];
2703
+ return {
2704
+ locale: locale === "en" || locale === "zh-CN" ? locale : fallbackLocale,
2705
+ tab: isTabKey(tab) ? tab : "overview",
2706
+ };
2707
+ }
2708
+ }
2709
+ }
2710
+ catch {
2711
+ }
2712
+ return { locale: fallbackLocale, tab: "overview" };
2713
+ }
2714
+ function savePrefs(prefs) {
2715
+ try {
2716
+ window.localStorage.setItem(PREFS_STORAGE_KEY, JSON.stringify(prefs));
2717
+ }
2718
+ catch {
2719
+ }
2720
+ }
2721
+ function Notice(props) {
2722
+ const t = Embassy.useT();
2723
+ return (React.createElement("div", { className: "notice", "data-tone": props.tone, role: "status" },
2724
+ React.createElement("span", null, props.text),
2725
+ React.createElement("button", { type: "button", className: "toggle-link", onClick: props.onDismiss }, t("app.hide"))));
2726
+ }
2727
+ function Shell(props) {
2728
+ const t = Embassy.useT();
2729
+ const [locale, setLocale] = Embassy.useLocale();
2730
+ const [tab, setTab] = React.useState(props.initialTab);
2731
+ const [latest, setLatest] = React.useState(undefined);
2732
+ const [connectionState, setConnectionState] = React.useState("connecting");
2733
+ const [lastFrameAtMs, setLastFrameAtMs] = React.useState(undefined);
2734
+ const [notices, setNotices] = React.useState({
2735
+ reset: false,
2736
+ missedFrames: false,
2737
+ });
2738
+ const [preset, setPreset] = React.useState(undefined);
2739
+ const [query, setQuery] = React.useState("");
2740
+ const nowMs = Embassy.useNowMs();
2741
+ const protocolRef = React.useRef(undefined);
2742
+ const tabRefs = React.useRef([]);
2743
+ React.useEffect(() => {
2744
+ const protocol = Embassy.createProtocol({
2745
+ onEvent: (event) => {
2746
+ setLatest(event);
2747
+ setLastFrameAtMs(Date.now());
2748
+ },
2749
+ onConnectionState: setConnectionState,
2750
+ onNotice: (kind) => {
2751
+ setNotices((previous) => ({ ...previous, [kind]: true }));
2752
+ },
2753
+ });
2754
+ protocolRef.current = protocol;
2755
+ protocol.start();
2756
+ return () => {
2757
+ protocolRef.current = undefined;
2758
+ protocol.pause();
2759
+ };
2760
+ }, []);
2761
+ React.useEffect(() => {
2762
+ document.documentElement.lang = locale;
2763
+ }, [locale]);
2764
+ React.useEffect(() => {
2765
+ savePrefs({ locale, tab });
2766
+ }, [locale, tab]);
2767
+ const goDeliveries = React.useCallback((next) => {
2768
+ setPreset(next);
2769
+ setTab("deliveries");
2770
+ }, []);
2771
+ const clearPreset = React.useCallback(() => {
2772
+ setPreset(undefined);
2773
+ }, []);
2774
+ const runAction = React.useCallback(async (action) => protocolRef.current?.executeAction(action) ?? {
2775
+ ok: false,
2776
+ code: "unavailable",
2777
+ }, []);
2778
+ const dismissNotice = (kind) => {
2779
+ setNotices((previous) => ({ ...previous, [kind]: false }));
2780
+ };
2781
+ const onSearchKeyDown = (event) => {
2782
+ if (event.key !== "Enter")
2783
+ return;
2784
+ const token = query.trim();
2785
+ if (token === "")
2786
+ return;
2787
+ setPreset({ token });
2788
+ setTab("deliveries");
2789
+ setQuery("");
2790
+ };
2791
+ const onTabKeyDown = (event) => {
2792
+ const currentIndex = TAB_KEYS.indexOf(tab);
2793
+ let nextIndex;
2794
+ if (event.key === "ArrowRight") {
2795
+ nextIndex = (currentIndex + 1) % TAB_KEYS.length;
2796
+ }
2797
+ else if (event.key === "ArrowLeft") {
2798
+ nextIndex = (currentIndex - 1 + TAB_KEYS.length) % TAB_KEYS.length;
2799
+ }
2800
+ else if (event.key === "Home") {
2801
+ nextIndex = 0;
2802
+ }
2803
+ else if (event.key === "End") {
2804
+ nextIndex = TAB_KEYS.length - 1;
2805
+ }
2806
+ if (nextIndex === undefined)
2807
+ return;
2808
+ event.preventDefault();
2809
+ const nextTab = TAB_KEYS[nextIndex];
2810
+ if (nextTab === undefined)
2811
+ return;
2812
+ setTab(nextTab);
2813
+ tabRefs.current[nextIndex]?.focus();
2814
+ };
2815
+ const generatedAt = latest?.model.generatedAt;
2816
+ const staleAgeMs = lastFrameAtMs === undefined ? undefined : nowMs - lastFrameAtMs;
2817
+ const hasNotices = notices.reset || notices.missedFrames;
2818
+ function renderActiveTab() {
2819
+ if (latest === undefined) {
2820
+ return (React.createElement("div", { className: "empty-state" },
2821
+ React.createElement("p", { className: "empty-state__text" }, t(`live.connection.${connectionState}`))));
2822
+ }
2823
+ const model = latest.model;
2824
+ switch (tab) {
2825
+ case "overview":
2826
+ return (React.createElement(Embassy.OverviewTab, { data: Embassy.adapter.overviewProps(model, nowMs), onViewDeliveries: goDeliveries }));
2827
+ case "deliveries":
2828
+ return (React.createElement(Embassy.DeliveriesTab, { groups: Embassy.adapter.deliveriesGroups(model), watches: model.watches ?? [], watchEvents: model.watchEvents ?? [], omissions: model.omissions, preset: preset, clearPreset: clearPreset }));
2829
+ case "routes":
2830
+ return (React.createElement(Embassy.RoutesTab, { data: Embassy.adapter.routesProps(model, nowMs), actionsEnabled: connectionState === "connected", onAction: runAction }));
2831
+ case "activity":
2832
+ return (React.createElement(Embassy.ActivityTab, { rows: Embassy.adapter.activityRows(model), omissions: model.omissions }));
2833
+ case "diagnostics":
2834
+ return React.createElement(Embassy.DiagnosticsTab, { data: Embassy.adapter.diagnosticsProps(model) });
2835
+ }
2836
+ }
2837
+ return (React.createElement("div", { className: "app-shell" },
2838
+ React.createElement("header", { className: "app-header" },
2839
+ React.createElement("div", { className: "header-bar" },
2840
+ React.createElement("div", { className: "brand" },
2841
+ React.createElement("svg", { width: "26", height: "26", viewBox: "0 0 64 64", fill: "none", "aria-hidden": "true", className: "brand-mark" },
2842
+ React.createElement("path", { d: "M32 16 V5 L41 7.5 L32 10", fill: "#f4a259" }),
2843
+ React.createElement("path", { d: "M21.5 55 V32 C21.5 23 42.5 23 42.5 32 V55 Z", fill: "#f4a259" }),
2844
+ React.createElement("path", { d: "M14 55 V30.5 C14 17 50 17 50 30.5 V55", stroke: "#17171c", strokeWidth: "3.4", strokeLinecap: "round" }),
2845
+ React.createElement("path", { d: "M10 55.5 H54", stroke: "#17171c", strokeWidth: "3", strokeLinecap: "round" })),
2846
+ React.createElement("span", { className: "brand-wordmark" }, "Embassy"),
2847
+ React.createElement("span", { className: "mono-label" }, t("live.label")),
2848
+ latest === undefined ? null : (React.createElement("span", { className: "brand-posture" },
2849
+ React.createElement(Embassy.StateChip, { domain: "overall", state: latest.model.overall, small: true })))),
2850
+ React.createElement("div", { className: "header-spacer" }),
2851
+ React.createElement("input", { type: "search", className: "text-input header-search", value: query, onChange: (event) => {
2852
+ setQuery(event.target.value);
2853
+ }, onKeyDown: onSearchKeyDown, "aria-label": t("app.search.label"), placeholder: t("app.search.placeholder") }),
2854
+ React.createElement("div", { className: "header-meta" },
2855
+ React.createElement("span", { className: "as-of", title: generatedAt }, t("app.asOf", {
2856
+ time: generatedAt === undefined
2857
+ ? t("time.unavailable")
2858
+ : Embassy.fmtAbs(generatedAt, locale),
2859
+ })),
2860
+ React.createElement("span", { className: "conn-status", "aria-live": "polite" },
2861
+ React.createElement(Embassy.StateChip, { domain: "connection", state: connectionState, label: t(`live.connection.${connectionState}`), small: true })),
2862
+ React.createElement("div", { className: "lang-toggle" },
2863
+ React.createElement("button", { type: "button", className: "lang-toggle__btn", "aria-pressed": locale === "en", onClick: () => {
2864
+ setLocale("en");
2865
+ } }, "EN"),
2866
+ React.createElement("button", { type: "button", className: "lang-toggle__btn", "aria-pressed": locale === "zh-CN", onClick: () => {
2867
+ setLocale("zh-CN");
2868
+ } }, "\u4E2D\u6587"))),
2869
+ React.createElement("div", { className: "header-subrow" },
2870
+ staleAgeMs !== undefined && staleAgeMs >= STALE_NOTICE_MS ? (React.createElement("p", { className: connectionState === "connected"
2871
+ ? "staleness staleness--quiet"
2872
+ : "staleness", role: "status" }, connectionState === "connected"
2873
+ ? t("app.staleQuiet", { age: Embassy.fmtAge(staleAgeMs) })
2874
+ : t("app.stale", { age: Embassy.fmtAge(staleAgeMs) }))) : null,
2875
+ React.createElement("span", { className: "conn-controls" },
2876
+ React.createElement("button", { type: "button", className: "btn btn--ghost", onClick: () => protocolRef.current?.pause() }, t("live.control.pause")),
2877
+ React.createElement("button", { type: "button", className: "btn btn--ghost", onClick: () => protocolRef.current?.reconnect() }, t("live.control.reconnect")),
2878
+ React.createElement("button", { type: "button", className: "btn btn--ghost", onClick: () => protocolRef.current?.readNow() }, t("live.control.refresh"))))),
2879
+ 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) => {
2880
+ tabRefs.current[index] = element;
2881
+ }, onClick: () => {
2882
+ setTab(key);
2883
+ }, onKeyDown: onTabKeyDown }, t(`app.tab.${key}`)))))),
2884
+ React.createElement("main", { className: "app-main" },
2885
+ React.createElement("div", { className: "stack-lg" },
2886
+ hasNotices ? (React.createElement("div", { className: "stack" },
2887
+ notices.reset ? (React.createElement(Notice, { tone: "info", text: t("live.stream.reset"), onDismiss: () => {
2888
+ dismissNotice("reset");
2889
+ } })) : null,
2890
+ notices.missedFrames ? (React.createElement(Notice, { tone: "warning", text: t("app.missedFrames"), onDismiss: () => {
2891
+ dismissNotice("missedFrames");
2892
+ } })) : null)) : null,
2893
+ React.createElement("div", { role: "tabpanel", id: `panel-${tab}`, "aria-labelledby": `tab-${tab}`, tabIndex: 0 }, renderActiveTab()))),
2894
+ React.createElement("footer", { className: "app-footer" },
2895
+ React.createElement("span", { className: "mono-label" }, t("live.action.authorityLabel")),
2896
+ React.createElement("p", null, t("live.action.authorityBody")),
2897
+ React.createElement("p", { className: "footer-diagnostic" },
2898
+ t("live.metric.revision"),
2899
+ ":",
2900
+ " ",
2901
+ latest === undefined ? "—" : String(latest.streamRevision)))));
2902
+ }
2903
+ function App() {
2904
+ const boot = window.EMBASSY_BOOT;
2905
+ const [initialPrefs] = React.useState(() => loadPrefs(boot.locale));
2906
+ return (React.createElement(Embassy.I18nProvider, { copy: boot.copy, initialLocale: initialPrefs.locale },
2907
+ React.createElement(Shell, { initialTab: initialPrefs.tab })));
2908
+ }
2909
+ Embassy.App = App;
2910
+ const rootElement = document.getElementById("root");
2911
+ if (rootElement !== null) {
2912
+ ReactDOM.createRoot(rootElement).render(React.createElement(App, null));
2913
+ }
2914
+ })(Embassy || (Embassy = {}));