@katyella/legio 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (219) hide show
  1. package/CHANGELOG.md +422 -0
  2. package/LICENSE +21 -0
  3. package/README.md +555 -0
  4. package/agents/builder.md +141 -0
  5. package/agents/coordinator.md +351 -0
  6. package/agents/cto.md +196 -0
  7. package/agents/gateway.md +276 -0
  8. package/agents/lead.md +281 -0
  9. package/agents/merger.md +156 -0
  10. package/agents/monitor.md +212 -0
  11. package/agents/reviewer.md +142 -0
  12. package/agents/scout.md +131 -0
  13. package/agents/supervisor.md +416 -0
  14. package/bin/legio.mjs +38 -0
  15. package/package.json +77 -0
  16. package/src/agents/checkpoint.test.ts +88 -0
  17. package/src/agents/checkpoint.ts +102 -0
  18. package/src/agents/hooks-deployer.test.ts +1820 -0
  19. package/src/agents/hooks-deployer.ts +574 -0
  20. package/src/agents/identity.test.ts +614 -0
  21. package/src/agents/identity.ts +385 -0
  22. package/src/agents/lifecycle.test.ts +202 -0
  23. package/src/agents/lifecycle.ts +184 -0
  24. package/src/agents/manifest.test.ts +558 -0
  25. package/src/agents/manifest.ts +297 -0
  26. package/src/agents/overlay.test.ts +592 -0
  27. package/src/agents/overlay.ts +316 -0
  28. package/src/beads/client.test.ts +210 -0
  29. package/src/beads/client.ts +227 -0
  30. package/src/beads/molecules.test.ts +320 -0
  31. package/src/beads/molecules.ts +209 -0
  32. package/src/commands/agents.test.ts +325 -0
  33. package/src/commands/agents.ts +286 -0
  34. package/src/commands/clean.test.ts +730 -0
  35. package/src/commands/clean.ts +653 -0
  36. package/src/commands/completions.test.ts +346 -0
  37. package/src/commands/completions.ts +950 -0
  38. package/src/commands/coordinator.test.ts +1524 -0
  39. package/src/commands/coordinator.ts +880 -0
  40. package/src/commands/costs.test.ts +1015 -0
  41. package/src/commands/costs.ts +473 -0
  42. package/src/commands/dashboard.test.ts +94 -0
  43. package/src/commands/dashboard.ts +607 -0
  44. package/src/commands/doctor.test.ts +295 -0
  45. package/src/commands/doctor.ts +213 -0
  46. package/src/commands/down.test.ts +308 -0
  47. package/src/commands/down.ts +124 -0
  48. package/src/commands/errors.test.ts +648 -0
  49. package/src/commands/errors.ts +255 -0
  50. package/src/commands/feed.test.ts +579 -0
  51. package/src/commands/feed.ts +368 -0
  52. package/src/commands/gateway.test.ts +698 -0
  53. package/src/commands/gateway.ts +419 -0
  54. package/src/commands/group.test.ts +262 -0
  55. package/src/commands/group.ts +539 -0
  56. package/src/commands/hooks.test.ts +292 -0
  57. package/src/commands/hooks.ts +210 -0
  58. package/src/commands/init.test.ts +211 -0
  59. package/src/commands/init.ts +622 -0
  60. package/src/commands/inspect.test.ts +670 -0
  61. package/src/commands/inspect.ts +455 -0
  62. package/src/commands/log.test.ts +1556 -0
  63. package/src/commands/log.ts +752 -0
  64. package/src/commands/logs.test.ts +379 -0
  65. package/src/commands/logs.ts +544 -0
  66. package/src/commands/mail.test.ts +1726 -0
  67. package/src/commands/mail.ts +926 -0
  68. package/src/commands/merge.test.ts +676 -0
  69. package/src/commands/merge.ts +374 -0
  70. package/src/commands/metrics.test.ts +444 -0
  71. package/src/commands/metrics.ts +150 -0
  72. package/src/commands/monitor.test.ts +151 -0
  73. package/src/commands/monitor.ts +394 -0
  74. package/src/commands/nudge.test.ts +230 -0
  75. package/src/commands/nudge.ts +373 -0
  76. package/src/commands/prime.test.ts +467 -0
  77. package/src/commands/prime.ts +386 -0
  78. package/src/commands/replay.test.ts +742 -0
  79. package/src/commands/replay.ts +367 -0
  80. package/src/commands/run.test.ts +443 -0
  81. package/src/commands/run.ts +365 -0
  82. package/src/commands/server.test.ts +626 -0
  83. package/src/commands/server.ts +298 -0
  84. package/src/commands/sling.test.ts +810 -0
  85. package/src/commands/sling.ts +700 -0
  86. package/src/commands/spec.test.ts +206 -0
  87. package/src/commands/spec.ts +171 -0
  88. package/src/commands/status.test.ts +276 -0
  89. package/src/commands/status.ts +339 -0
  90. package/src/commands/stop.test.ts +357 -0
  91. package/src/commands/stop.ts +119 -0
  92. package/src/commands/supervisor.test.ts +186 -0
  93. package/src/commands/supervisor.ts +544 -0
  94. package/src/commands/trace.test.ts +746 -0
  95. package/src/commands/trace.ts +332 -0
  96. package/src/commands/up.test.ts +597 -0
  97. package/src/commands/up.ts +275 -0
  98. package/src/commands/watch.test.ts +152 -0
  99. package/src/commands/watch.ts +238 -0
  100. package/src/commands/worktree.test.ts +648 -0
  101. package/src/commands/worktree.ts +266 -0
  102. package/src/config.test.ts +496 -0
  103. package/src/config.ts +616 -0
  104. package/src/doctor/agents.test.ts +448 -0
  105. package/src/doctor/agents.ts +396 -0
  106. package/src/doctor/config-check.test.ts +184 -0
  107. package/src/doctor/config-check.ts +185 -0
  108. package/src/doctor/consistency.test.ts +645 -0
  109. package/src/doctor/consistency.ts +294 -0
  110. package/src/doctor/databases.test.ts +284 -0
  111. package/src/doctor/databases.ts +211 -0
  112. package/src/doctor/dependencies.test.ts +150 -0
  113. package/src/doctor/dependencies.ts +179 -0
  114. package/src/doctor/logs.test.ts +244 -0
  115. package/src/doctor/logs.ts +295 -0
  116. package/src/doctor/merge-queue.test.ts +210 -0
  117. package/src/doctor/merge-queue.ts +144 -0
  118. package/src/doctor/structure.test.ts +285 -0
  119. package/src/doctor/structure.ts +195 -0
  120. package/src/doctor/types.ts +37 -0
  121. package/src/doctor/version.test.ts +130 -0
  122. package/src/doctor/version.ts +131 -0
  123. package/src/e2e/chat-flow.test.ts +346 -0
  124. package/src/e2e/init-sling-lifecycle.test.ts +288 -0
  125. package/src/errors.test.ts +21 -0
  126. package/src/errors.ts +246 -0
  127. package/src/events/store.test.ts +660 -0
  128. package/src/events/store.ts +344 -0
  129. package/src/events/tool-filter.test.ts +330 -0
  130. package/src/events/tool-filter.ts +126 -0
  131. package/src/global-setup.ts +14 -0
  132. package/src/index.ts +339 -0
  133. package/src/insights/analyzer.test.ts +466 -0
  134. package/src/insights/analyzer.ts +203 -0
  135. package/src/logging/color.test.ts +118 -0
  136. package/src/logging/color.ts +71 -0
  137. package/src/logging/logger.test.ts +812 -0
  138. package/src/logging/logger.ts +266 -0
  139. package/src/logging/reporter.test.ts +258 -0
  140. package/src/logging/reporter.ts +109 -0
  141. package/src/logging/sanitizer.test.ts +190 -0
  142. package/src/logging/sanitizer.ts +57 -0
  143. package/src/mail/broadcast.test.ts +203 -0
  144. package/src/mail/broadcast.ts +92 -0
  145. package/src/mail/client.test.ts +873 -0
  146. package/src/mail/client.ts +236 -0
  147. package/src/mail/store.test.ts +815 -0
  148. package/src/mail/store.ts +402 -0
  149. package/src/merge/queue.test.ts +449 -0
  150. package/src/merge/queue.ts +262 -0
  151. package/src/merge/resolver.test.ts +1453 -0
  152. package/src/merge/resolver.ts +759 -0
  153. package/src/metrics/store.test.ts +1167 -0
  154. package/src/metrics/store.ts +511 -0
  155. package/src/metrics/summary.test.ts +397 -0
  156. package/src/metrics/summary.ts +178 -0
  157. package/src/metrics/transcript.test.ts +643 -0
  158. package/src/metrics/transcript.ts +351 -0
  159. package/src/mulch/client.test.ts +547 -0
  160. package/src/mulch/client.ts +416 -0
  161. package/src/server/audit-store.test.ts +384 -0
  162. package/src/server/audit-store.ts +257 -0
  163. package/src/server/headless.test.ts +180 -0
  164. package/src/server/headless.ts +151 -0
  165. package/src/server/index.test.ts +241 -0
  166. package/src/server/index.ts +317 -0
  167. package/src/server/public/app.js +187 -0
  168. package/src/server/public/apple-touch-icon.png +0 -0
  169. package/src/server/public/components/agent-badge.js +37 -0
  170. package/src/server/public/components/data-table.js +114 -0
  171. package/src/server/public/components/gateway-chat.js +256 -0
  172. package/src/server/public/components/issue-card.js +96 -0
  173. package/src/server/public/components/layout.js +88 -0
  174. package/src/server/public/components/message-bubble.js +120 -0
  175. package/src/server/public/components/stat-card.js +26 -0
  176. package/src/server/public/components/terminal-panel.js +140 -0
  177. package/src/server/public/favicon-16.png +0 -0
  178. package/src/server/public/favicon-32.png +0 -0
  179. package/src/server/public/favicon.ico +0 -0
  180. package/src/server/public/favicon.png +0 -0
  181. package/src/server/public/index.html +64 -0
  182. package/src/server/public/lib/api.js +35 -0
  183. package/src/server/public/lib/markdown.js +8 -0
  184. package/src/server/public/lib/preact-setup.js +8 -0
  185. package/src/server/public/lib/state.js +99 -0
  186. package/src/server/public/lib/utils.js +309 -0
  187. package/src/server/public/lib/ws.js +79 -0
  188. package/src/server/public/views/chat.js +983 -0
  189. package/src/server/public/views/costs.js +692 -0
  190. package/src/server/public/views/dashboard.js +781 -0
  191. package/src/server/public/views/gateway-chat.js +622 -0
  192. package/src/server/public/views/inspect.js +399 -0
  193. package/src/server/public/views/issues.js +470 -0
  194. package/src/server/public/views/setup.js +94 -0
  195. package/src/server/public/views/task-detail.js +422 -0
  196. package/src/server/routes.test.ts +3816 -0
  197. package/src/server/routes.ts +1964 -0
  198. package/src/server/websocket.test.ts +288 -0
  199. package/src/server/websocket.ts +196 -0
  200. package/src/sessions/compat.test.ts +109 -0
  201. package/src/sessions/compat.ts +17 -0
  202. package/src/sessions/store.test.ts +969 -0
  203. package/src/sessions/store.ts +480 -0
  204. package/src/test-helpers.test.ts +97 -0
  205. package/src/test-helpers.ts +143 -0
  206. package/src/types.ts +708 -0
  207. package/src/watchdog/daemon.test.ts +1233 -0
  208. package/src/watchdog/daemon.ts +533 -0
  209. package/src/watchdog/health.test.ts +371 -0
  210. package/src/watchdog/health.ts +248 -0
  211. package/src/watchdog/triage.test.ts +162 -0
  212. package/src/watchdog/triage.ts +193 -0
  213. package/src/worktree/manager.test.ts +444 -0
  214. package/src/worktree/manager.ts +224 -0
  215. package/src/worktree/tmux.test.ts +1238 -0
  216. package/src/worktree/tmux.ts +644 -0
  217. package/templates/CLAUDE.md.tmpl +89 -0
  218. package/templates/hooks.json.tmpl +132 -0
  219. package/templates/overlay.md.tmpl +79 -0
@@ -0,0 +1,332 @@
1
+ /**
2
+ * CLI command: legio trace <target> [--json] [--since <ts>] [--until <ts>] [--limit <n>]
3
+ *
4
+ * Shows a chronological timeline of events for an agent or bead task.
5
+ * Target can be an agent name or a bead ID (resolved to agent name via SessionStore).
6
+ */
7
+
8
+ import { access } from "node:fs/promises";
9
+ import { join } from "node:path";
10
+ import { loadConfig } from "../config.ts";
11
+ import { ValidationError } from "../errors.ts";
12
+ import { createEventStore } from "../events/store.ts";
13
+ import { color } from "../logging/color.ts";
14
+ import { openSessionStore } from "../sessions/compat.ts";
15
+ import type { EventType, StoredEvent } from "../types.ts";
16
+
17
+ /** Labels and colors for each event type. */
18
+ const EVENT_LABELS: Record<EventType, { label: string; color: string }> = {
19
+ tool_start: { label: "TOOL START", color: color.blue },
20
+ tool_end: { label: "TOOL END ", color: color.blue },
21
+ session_start: { label: "SESSION +", color: color.green },
22
+ session_end: { label: "SESSION -", color: color.yellow },
23
+ mail_sent: { label: "MAIL SENT ", color: color.cyan },
24
+ mail_received: { label: "MAIL RECV ", color: color.cyan },
25
+ spawn: { label: "SPAWN ", color: color.magenta },
26
+ error: { label: "ERROR ", color: color.red },
27
+ custom: { label: "CUSTOM ", color: color.gray },
28
+ };
29
+
30
+ /**
31
+ * Parse a named flag value from args.
32
+ */
33
+ function getFlag(args: string[], flag: string): string | undefined {
34
+ const idx = args.indexOf(flag);
35
+ if (idx === -1 || idx + 1 >= args.length) {
36
+ return undefined;
37
+ }
38
+ return args[idx + 1];
39
+ }
40
+
41
+ function hasFlag(args: string[], flag: string): boolean {
42
+ return args.includes(flag);
43
+ }
44
+
45
+ /**
46
+ * Detect whether a target string looks like a bead ID.
47
+ * Bead IDs follow the pattern: word-alphanumeric (e.g., "legio-rj1k", "myproject-abc1").
48
+ */
49
+ function looksLikeBeadId(target: string): boolean {
50
+ return /^[a-z][a-z0-9]*-[a-z0-9]{3,}$/i.test(target);
51
+ }
52
+
53
+ /**
54
+ * Format a relative time string from a timestamp.
55
+ * Returns strings like "2m ago", "1h ago", "3d ago".
56
+ */
57
+ function formatRelativeTime(timestamp: string): string {
58
+ const eventTime = new Date(timestamp).getTime();
59
+ const now = Date.now();
60
+ const diffMs = now - eventTime;
61
+
62
+ if (diffMs < 0) return "just now";
63
+
64
+ const seconds = Math.floor(diffMs / 1000);
65
+ if (seconds < 60) return `${seconds}s ago`;
66
+
67
+ const minutes = Math.floor(seconds / 60);
68
+ if (minutes < 60) return `${minutes}m ago`;
69
+
70
+ const hours = Math.floor(minutes / 60);
71
+ if (hours < 24) return `${hours}h ago`;
72
+
73
+ const days = Math.floor(hours / 24);
74
+ return `${days}d ago`;
75
+ }
76
+
77
+ /**
78
+ * Format an absolute time from an ISO timestamp.
79
+ * Returns "HH:MM:SS" portion.
80
+ */
81
+ function formatAbsoluteTime(timestamp: string): string {
82
+ const match = /T(\d{2}:\d{2}:\d{2})/.exec(timestamp);
83
+ if (match?.[1]) {
84
+ return match[1];
85
+ }
86
+ return timestamp;
87
+ }
88
+
89
+ /**
90
+ * Format the date portion of an ISO timestamp.
91
+ * Returns "YYYY-MM-DD".
92
+ */
93
+ function formatDate(timestamp: string): string {
94
+ const match = /^(\d{4}-\d{2}-\d{2})/.exec(timestamp);
95
+ if (match?.[1]) {
96
+ return match[1];
97
+ }
98
+ return "";
99
+ }
100
+
101
+ /**
102
+ * Build a detail string for a timeline event based on its type and fields.
103
+ */
104
+ function buildEventDetail(event: StoredEvent): string {
105
+ const parts: string[] = [];
106
+
107
+ if (event.toolName) {
108
+ parts.push(`tool=${event.toolName}`);
109
+ }
110
+
111
+ if (event.toolDurationMs !== null) {
112
+ parts.push(`duration=${event.toolDurationMs}ms`);
113
+ }
114
+
115
+ if (event.data) {
116
+ try {
117
+ const parsed: unknown = JSON.parse(event.data);
118
+ if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
119
+ const data = parsed as Record<string, unknown>;
120
+ for (const [key, value] of Object.entries(data)) {
121
+ if (value !== null && value !== undefined) {
122
+ const strValue = typeof value === "string" ? value : JSON.stringify(value);
123
+ // Truncate long values
124
+ const truncated = strValue.length > 80 ? `${strValue.slice(0, 77)}...` : strValue;
125
+ parts.push(`${key}=${truncated}`);
126
+ }
127
+ }
128
+ }
129
+ } catch {
130
+ // data is not valid JSON; show it raw if short enough
131
+ if (event.data.length <= 80) {
132
+ parts.push(event.data);
133
+ }
134
+ }
135
+ }
136
+
137
+ return parts.join(" ");
138
+ }
139
+
140
+ /**
141
+ * Print events as a formatted timeline with ANSI colors.
142
+ */
143
+ function printTimeline(events: StoredEvent[], agentName: string, useAbsoluteTime: boolean): void {
144
+ const w = process.stdout.write.bind(process.stdout);
145
+
146
+ w(`${color.bold}Timeline for ${agentName}${color.reset}\n`);
147
+ w(`${"=".repeat(70)}\n`);
148
+
149
+ if (events.length === 0) {
150
+ w(`${color.dim}No events found.${color.reset}\n`);
151
+ return;
152
+ }
153
+
154
+ w(`${color.dim}${events.length} event${events.length === 1 ? "" : "s"}${color.reset}\n\n`);
155
+
156
+ let lastDate = "";
157
+
158
+ for (const event of events) {
159
+ // Print date separator when the date changes
160
+ const date = formatDate(event.createdAt);
161
+ if (date && date !== lastDate) {
162
+ if (lastDate !== "") {
163
+ w("\n");
164
+ }
165
+ w(`${color.dim}--- ${date} ---${color.reset}\n`);
166
+ lastDate = date;
167
+ }
168
+
169
+ const timeStr = useAbsoluteTime
170
+ ? formatAbsoluteTime(event.createdAt)
171
+ : formatRelativeTime(event.createdAt);
172
+
173
+ const eventInfo = EVENT_LABELS[event.eventType] ?? {
174
+ label: event.eventType.padEnd(10),
175
+ color: color.gray,
176
+ };
177
+
178
+ const levelColor =
179
+ event.level === "error" ? color.red : event.level === "warn" ? color.yellow : "";
180
+ const levelReset = levelColor ? color.reset : "";
181
+
182
+ const detail = buildEventDetail(event);
183
+ const detailSuffix = detail ? ` ${color.dim}${detail}${color.reset}` : "";
184
+
185
+ const agentLabel =
186
+ event.agentName !== agentName ? ` ${color.dim}[${event.agentName}]${color.reset}` : "";
187
+
188
+ w(
189
+ `${color.dim}${timeStr.padStart(10)}${color.reset} ` +
190
+ `${levelColor}${eventInfo.color}${color.bold}${eventInfo.label}${color.reset}${levelReset}` +
191
+ `${agentLabel}${detailSuffix}\n`,
192
+ );
193
+ }
194
+ }
195
+
196
+ const TRACE_HELP = `legio trace -- Show chronological timeline for an agent or bead
197
+
198
+ Usage: legio trace <target> [options]
199
+
200
+ Arguments:
201
+ <target> Agent name or bead ID
202
+
203
+ Options:
204
+ --json Output as JSON array of StoredEvent objects
205
+ --since <timestamp> Start time filter (ISO 8601)
206
+ --until <timestamp> End time filter (ISO 8601)
207
+ --limit <n> Max events to show (default: 100)
208
+ --help, -h Show this help`;
209
+
210
+ /**
211
+ * Entry point for `legio trace <target> [--json] [--since] [--until] [--limit]`.
212
+ */
213
+ export async function traceCommand(args: string[]): Promise<void> {
214
+ if (args.includes("--help") || args.includes("-h")) {
215
+ process.stdout.write(`${TRACE_HELP}\n`);
216
+ return;
217
+ }
218
+
219
+ // Extract positional target: first arg that is not a flag or flag value
220
+ const flagsWithValues = new Set(["--since", "--until", "--limit"]);
221
+ const booleanFlags = new Set(["--json", "--help", "-h"]);
222
+ let target: string | undefined;
223
+ for (let i = 0; i < args.length; i++) {
224
+ const arg = args[i];
225
+ if (arg === undefined) continue;
226
+ if (booleanFlags.has(arg)) continue;
227
+ if (flagsWithValues.has(arg)) {
228
+ i++; // skip the value
229
+ continue;
230
+ }
231
+ if (arg.startsWith("-")) continue;
232
+ target = arg;
233
+ break;
234
+ }
235
+
236
+ if (!target) {
237
+ throw new ValidationError("Missing target. Usage: legio trace <agent-name|bead-id>", {
238
+ field: "target",
239
+ });
240
+ }
241
+
242
+ const json = hasFlag(args, "--json");
243
+ const sinceStr = getFlag(args, "--since");
244
+ const untilStr = getFlag(args, "--until");
245
+ const limitStr = getFlag(args, "--limit");
246
+ const limit = limitStr ? Number.parseInt(limitStr, 10) : 100;
247
+
248
+ if (Number.isNaN(limit) || limit < 1) {
249
+ throw new ValidationError("--limit must be a positive integer", {
250
+ field: "limit",
251
+ value: limitStr,
252
+ });
253
+ }
254
+
255
+ // Validate timestamps if provided
256
+ if (sinceStr !== undefined && Number.isNaN(new Date(sinceStr).getTime())) {
257
+ throw new ValidationError("--since must be a valid ISO 8601 timestamp", {
258
+ field: "since",
259
+ value: sinceStr,
260
+ });
261
+ }
262
+ if (untilStr !== undefined && Number.isNaN(new Date(untilStr).getTime())) {
263
+ throw new ValidationError("--until must be a valid ISO 8601 timestamp", {
264
+ field: "until",
265
+ value: untilStr,
266
+ });
267
+ }
268
+
269
+ const cwd = process.cwd();
270
+ const config = await loadConfig(cwd);
271
+ const legioDir = join(config.project.root, ".legio");
272
+
273
+ // Resolve target to agent name
274
+ let agentName = target;
275
+
276
+ if (looksLikeBeadId(target)) {
277
+ // Try to resolve bead ID to agent name via SessionStore
278
+ const { store: sessionStore } = openSessionStore(legioDir);
279
+ try {
280
+ const allSessions = sessionStore.getAll();
281
+ const matchingSession = allSessions.find((s) => s.beadId === target);
282
+ if (matchingSession) {
283
+ agentName = matchingSession.agentName;
284
+ } else {
285
+ // No session found for this bead ID; treat it as an agent name anyway
286
+ // (the event query will return empty results if no events match)
287
+ agentName = target;
288
+ }
289
+ } finally {
290
+ sessionStore.close();
291
+ }
292
+ }
293
+
294
+ // Open event store and query events
295
+ const eventsDbPath = join(legioDir, "events.db");
296
+ let eventsDbExists = false;
297
+ try {
298
+ await access(eventsDbPath);
299
+ eventsDbExists = true;
300
+ } catch {
301
+ /* not found */
302
+ }
303
+ if (!eventsDbExists) {
304
+ if (json) {
305
+ process.stdout.write("[]\n");
306
+ } else {
307
+ process.stdout.write("No events data yet.\n");
308
+ }
309
+ return;
310
+ }
311
+
312
+ const eventStore = createEventStore(eventsDbPath);
313
+
314
+ try {
315
+ const events = eventStore.getByAgent(agentName, {
316
+ since: sinceStr,
317
+ until: untilStr,
318
+ limit,
319
+ });
320
+
321
+ if (json) {
322
+ process.stdout.write(`${JSON.stringify(events)}\n`);
323
+ return;
324
+ }
325
+
326
+ // Use absolute time if --since is specified, relative otherwise
327
+ const useAbsoluteTime = sinceStr !== undefined;
328
+ printTimeline(events, agentName, useAbsoluteTime);
329
+ } finally {
330
+ eventStore.close();
331
+ }
332
+ }