@atolis-hq/wake 0.3.86 → 0.3.88
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.
- package/dist/src/bootstrap/version.js +1 -1
- package/dist/src/persistence/filesystem/file-event-journal.js +224 -10
- package/dist/src/surfaces/web-assets/assets/index-BoJ0Mr2V.css +1 -0
- package/dist/src/surfaces/web-assets/assets/{index-BO92xZ4y.js → index-BuyqbjAi.js} +9 -9
- package/dist/src/surfaces/web-assets/index.html +2 -2
- package/package.json +1 -1
- package/dist/src/surfaces/web-assets/assets/index-DowyiaO5.css +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { appendFile, mkdir, readdir, readFile, stat } from 'node:fs/promises';
|
|
1
|
+
import { appendFile, mkdir, open, readdir, readFile, stat, writeFile } from 'node:fs/promises';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import { isDeepStrictEqual } from 'node:util';
|
|
4
4
|
import { decodeEventEnvelope, InProcessJournalChangeSignal, WrongExpectedSequenceError, } from '../../kernel/index.js';
|
|
@@ -69,16 +69,60 @@ export class FileEventJournal {
|
|
|
69
69
|
const file = `${day}.jsonl`;
|
|
70
70
|
await appendFile(join(directory, file), newEnvelopes.map((event) => JSON.stringify(event)).join('\n') + '\n', 'utf8');
|
|
71
71
|
await this.extendCache(file, current, newEnvelopes);
|
|
72
|
+
// The manifest is derived data. The event append is authoritative, so
|
|
73
|
+
// an index-write failure must not turn a successfully recorded event
|
|
74
|
+
// into a failed append.
|
|
75
|
+
await this.persistManifest().catch(() => undefined);
|
|
72
76
|
this.changeSignalSource.notify();
|
|
73
77
|
}
|
|
74
78
|
return finalizedEnvelopes;
|
|
75
79
|
});
|
|
76
80
|
}
|
|
81
|
+
// Reads via the persisted per-segment manifest when the in-memory cache is
|
|
82
|
+
// cold (fresh process, or an on-disk change this instance didn't make),
|
|
83
|
+
// parsing only the segment files that can hold matching events instead of
|
|
84
|
+
// the entire history. A missing or stale manifest degrades to scan()'s
|
|
85
|
+
// full parse rather than to incorrect data; scan() then rebuilds it.
|
|
77
86
|
async readStream(stream) {
|
|
78
|
-
|
|
87
|
+
const streamKey = key(stream);
|
|
88
|
+
const entries = await this.readCurrentEntries();
|
|
89
|
+
if (this.cached !== undefined && sameEntries(this.cached.entries, entries))
|
|
90
|
+
return this.cached.events.filter((event) => key(event.stream) === streamKey);
|
|
91
|
+
const manifest = await this.loadManifest();
|
|
92
|
+
if (manifest !== undefined && isCompleteIndex(manifest, entries)) {
|
|
93
|
+
try {
|
|
94
|
+
return await this.parseIndexedEntries(manifest.segments.flatMap((segment) => segment.events
|
|
95
|
+
.filter((event) => event.stream === streamKey)
|
|
96
|
+
.map((event) => ({ ...event, file: segment.file }))));
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
// The segment stats guarded the common stale-index case. If an index
|
|
100
|
+
// is nevertheless internally inconsistent, the JSONL remains the
|
|
101
|
+
// authority and retains the original read semantics.
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return (await this.scan(entries)).filter((event) => key(event.stream) === streamKey);
|
|
79
105
|
}
|
|
80
106
|
async readAll(after, limit = Number.POSITIVE_INFINITY) {
|
|
81
|
-
|
|
107
|
+
const entries = await this.readCurrentEntries();
|
|
108
|
+
if (this.cached !== undefined && sameEntries(this.cached.entries, entries))
|
|
109
|
+
return this.cached.events.filter((event) => event.globalPosition > after).slice(0, limit);
|
|
110
|
+
const manifest = await this.loadManifest();
|
|
111
|
+
if (manifest !== undefined && isCompleteIndex(manifest, entries)) {
|
|
112
|
+
try {
|
|
113
|
+
return await this.parseIndexedEntries(manifest.segments
|
|
114
|
+
.flatMap((segment) => segment.events
|
|
115
|
+
.filter((event) => event.globalPosition > after)
|
|
116
|
+
.map((event) => ({ ...event, file: segment.file })))
|
|
117
|
+
.slice(0, limit));
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
// See readStream(): never let a derived index change journal reads.
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return (await this.scan(entries))
|
|
124
|
+
.filter((event) => event.globalPosition > after)
|
|
125
|
+
.slice(0, limit);
|
|
82
126
|
}
|
|
83
127
|
async readLatest(beforeGlobalPosition, limit = Number.POSITIVE_INFINITY) {
|
|
84
128
|
const events = await this.scan();
|
|
@@ -104,19 +148,76 @@ export class FileEventJournal {
|
|
|
104
148
|
const entries = priorEntries.some((entry) => entry.file === file)
|
|
105
149
|
? priorEntries.map((entry) => (entry.file === file ? updatedEntry : entry))
|
|
106
150
|
: [...priorEntries, updatedEntry];
|
|
107
|
-
this.cached
|
|
151
|
+
const priorSegments = this.cached?.segments ?? [];
|
|
152
|
+
const existingSegment = priorSegments.find((segment) => segment.file === file);
|
|
153
|
+
let offset = existingSegment?.size ?? 0;
|
|
154
|
+
const indexedEvents = newEnvelopes.map((event) => {
|
|
155
|
+
const length = Buffer.byteLength(`${JSON.stringify(event)}\n`);
|
|
156
|
+
const indexed = {
|
|
157
|
+
globalPosition: event.globalPosition,
|
|
158
|
+
stream: key(event.stream),
|
|
159
|
+
offset,
|
|
160
|
+
length,
|
|
161
|
+
};
|
|
162
|
+
offset += length;
|
|
163
|
+
return indexed;
|
|
164
|
+
});
|
|
165
|
+
const segments = existingSegment
|
|
166
|
+
? priorSegments.map((segment) => segment.file === file
|
|
167
|
+
? {
|
|
168
|
+
...updatedEntry,
|
|
169
|
+
startPosition: segment.startPosition,
|
|
170
|
+
count: segment.count + newEnvelopes.length,
|
|
171
|
+
events: [...segment.events, ...indexedEvents],
|
|
172
|
+
}
|
|
173
|
+
: segment)
|
|
174
|
+
: [
|
|
175
|
+
...priorSegments,
|
|
176
|
+
{
|
|
177
|
+
...updatedEntry,
|
|
178
|
+
startPosition: priorEvents.length + 1,
|
|
179
|
+
count: newEnvelopes.length,
|
|
180
|
+
events: indexedEvents,
|
|
181
|
+
},
|
|
182
|
+
];
|
|
183
|
+
this.cached = { entries, events: [...priorEvents, ...newEnvelopes], segments };
|
|
108
184
|
}
|
|
109
|
-
|
|
185
|
+
manifestPath() {
|
|
186
|
+
return join(this.root, 'events', 'index-manifest.json');
|
|
187
|
+
}
|
|
188
|
+
// Persisted alongside the segments, so any reader of the same on-disk
|
|
189
|
+
// journal — not just this instance — can skip straight to the segments
|
|
190
|
+
// that can hold what it's looking for on a cold cache. append() extends its
|
|
191
|
+
// contents from the concrete envelopes it has just written while locked.
|
|
192
|
+
async persistManifest() {
|
|
193
|
+
if (this.cached === undefined)
|
|
194
|
+
return;
|
|
195
|
+
const manifest = { segments: this.cached.segments };
|
|
196
|
+
await writeFile(this.manifestPath(), JSON.stringify(manifest), 'utf8');
|
|
197
|
+
}
|
|
198
|
+
async loadManifest() {
|
|
199
|
+
try {
|
|
200
|
+
const raw = await readFile(this.manifestPath(), 'utf8');
|
|
201
|
+
const parsed = JSON.parse(raw);
|
|
202
|
+
return isPersistedIndex(parsed) ? parsed : undefined;
|
|
203
|
+
}
|
|
204
|
+
catch {
|
|
205
|
+
// Missing, corrupt, or foreign-shaped index: degrade to a full scan,
|
|
206
|
+
// which is rebuilt by the next append.
|
|
207
|
+
return undefined;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
scan(precomputedEntries) {
|
|
110
211
|
if (this.inFlightScan !== undefined)
|
|
111
212
|
return this.inFlightScan;
|
|
112
|
-
const run = this.scanUncoalesced().finally(() => {
|
|
213
|
+
const run = this.scanUncoalesced(precomputedEntries).finally(() => {
|
|
113
214
|
if (this.inFlightScan === run)
|
|
114
215
|
this.inFlightScan = undefined;
|
|
115
216
|
});
|
|
116
217
|
this.inFlightScan = run;
|
|
117
218
|
return run;
|
|
118
219
|
}
|
|
119
|
-
async
|
|
220
|
+
async readCurrentEntries() {
|
|
120
221
|
const directory = join(this.root, 'events');
|
|
121
222
|
let files;
|
|
122
223
|
try {
|
|
@@ -129,17 +230,69 @@ export class FileEventJournal {
|
|
|
129
230
|
return [];
|
|
130
231
|
throw error;
|
|
131
232
|
}
|
|
132
|
-
|
|
233
|
+
return Promise.all(files.map(async (file) => {
|
|
133
234
|
const info = await stat(join(directory, file));
|
|
134
235
|
return { file, size: info.size, mtimeMs: info.mtimeMs };
|
|
135
236
|
}));
|
|
237
|
+
}
|
|
238
|
+
// Reads exactly the indexed JSONL records. Byte offsets make tail reads and
|
|
239
|
+
// stream reads proportional to the matching events, not segment size.
|
|
240
|
+
async parseIndexedEntries(entries) {
|
|
241
|
+
const directory = join(this.root, 'events');
|
|
242
|
+
const events = [];
|
|
243
|
+
const grouped = new Map();
|
|
244
|
+
for (const entry of entries) {
|
|
245
|
+
const group = grouped.get(entry.file) ?? [];
|
|
246
|
+
group.push(entry);
|
|
247
|
+
grouped.set(entry.file, group);
|
|
248
|
+
}
|
|
249
|
+
for (const [file, indexedEvents] of grouped) {
|
|
250
|
+
const handle = await open(join(directory, file), 'r');
|
|
251
|
+
try {
|
|
252
|
+
for (const indexed of indexedEvents) {
|
|
253
|
+
const buffer = Buffer.alloc(indexed.length);
|
|
254
|
+
const { bytesRead } = await handle.read(buffer, 0, indexed.length, indexed.offset);
|
|
255
|
+
events.push(this.decodeIndexedRecord(file, indexed, buffer, bytesRead));
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
finally {
|
|
259
|
+
await handle.close();
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
return events;
|
|
263
|
+
}
|
|
264
|
+
decodeIndexedRecord(file, indexed, buffer, bytesRead) {
|
|
265
|
+
if (bytesRead !== indexed.length || buffer.at(-1) !== 10)
|
|
266
|
+
throw new Error(`Incomplete indexed record in ${file}`);
|
|
267
|
+
let input;
|
|
268
|
+
try {
|
|
269
|
+
input = JSON.parse(buffer.toString('utf8'));
|
|
270
|
+
const event = decodeEventEnvelope(input);
|
|
271
|
+
validateEnvelope(event, indexed.globalPosition);
|
|
272
|
+
if (key(event.stream) !== indexed.stream)
|
|
273
|
+
throw new Error('Indexed stream mismatch');
|
|
274
|
+
return event;
|
|
275
|
+
}
|
|
276
|
+
catch (error) {
|
|
277
|
+
const context = eventContext(input);
|
|
278
|
+
throw new Error(`Corrupt indexed event at ${file}:${indexed.globalPosition}${context}: ${error.message}`, { cause: error });
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
async scanUncoalesced(precomputedEntries) {
|
|
282
|
+
const directory = join(this.root, 'events');
|
|
283
|
+
const entries = precomputedEntries ?? (await this.readCurrentEntries());
|
|
136
284
|
if (this.cached !== undefined && sameEntries(this.cached.entries, entries))
|
|
137
285
|
return this.cached.events;
|
|
138
286
|
const events = [];
|
|
139
|
-
|
|
287
|
+
const segments = [];
|
|
288
|
+
for (const entry of entries) {
|
|
289
|
+
const { file } = entry;
|
|
140
290
|
const raw = await readFile(join(directory, file), 'utf8');
|
|
141
291
|
if (raw.length > 0 && !raw.endsWith('\n'))
|
|
142
292
|
throw new Error(`Incomplete trailing line in ${file}`);
|
|
293
|
+
const startPosition = events.length + 1;
|
|
294
|
+
const indexedEvents = [];
|
|
295
|
+
let offset = 0;
|
|
143
296
|
for (const [index, line] of raw.split('\n').slice(0, -1).entries()) {
|
|
144
297
|
let input;
|
|
145
298
|
try {
|
|
@@ -147,17 +300,78 @@ export class FileEventJournal {
|
|
|
147
300
|
const event = decodeEventEnvelope(input);
|
|
148
301
|
validateEnvelope(event, events.length + 1);
|
|
149
302
|
events.push(event);
|
|
303
|
+
indexedEvents.push({
|
|
304
|
+
globalPosition: event.globalPosition,
|
|
305
|
+
stream: key(event.stream),
|
|
306
|
+
offset,
|
|
307
|
+
length: Buffer.byteLength(`${line}\n`),
|
|
308
|
+
});
|
|
309
|
+
offset += Buffer.byteLength(`${line}\n`);
|
|
150
310
|
}
|
|
151
311
|
catch (error) {
|
|
152
312
|
const context = eventContext(input);
|
|
153
313
|
throw new Error(`Corrupt event at ${file}:${index + 1}${context}: ${error.message}`, { cause: error });
|
|
154
314
|
}
|
|
155
315
|
}
|
|
316
|
+
segments.push({
|
|
317
|
+
...entry,
|
|
318
|
+
startPosition,
|
|
319
|
+
count: events.length - startPosition + 1,
|
|
320
|
+
events: indexedEvents,
|
|
321
|
+
});
|
|
156
322
|
}
|
|
157
|
-
this.cached = { entries, events };
|
|
323
|
+
this.cached = { entries, events, segments };
|
|
158
324
|
return events;
|
|
159
325
|
}
|
|
160
326
|
}
|
|
327
|
+
function isPersistedIndex(value) {
|
|
328
|
+
return (typeof value === 'object' &&
|
|
329
|
+
value !== null &&
|
|
330
|
+
Array.isArray(value.segments) &&
|
|
331
|
+
value.segments.every(isSegmentInfo));
|
|
332
|
+
}
|
|
333
|
+
function isSegmentInfo(value) {
|
|
334
|
+
if (typeof value !== 'object' || value === null)
|
|
335
|
+
return false;
|
|
336
|
+
const segment = value;
|
|
337
|
+
return (typeof segment.file === 'string' &&
|
|
338
|
+
typeof segment.size === 'number' &&
|
|
339
|
+
typeof segment.mtimeMs === 'number' &&
|
|
340
|
+
typeof segment.startPosition === 'number' &&
|
|
341
|
+
typeof segment.count === 'number' &&
|
|
342
|
+
Array.isArray(segment.events) &&
|
|
343
|
+
segment.events.every(isIndexedEvent));
|
|
344
|
+
}
|
|
345
|
+
function isIndexedEvent(value) {
|
|
346
|
+
if (typeof value !== 'object' || value === null)
|
|
347
|
+
return false;
|
|
348
|
+
const event = value;
|
|
349
|
+
return (typeof event.globalPosition === 'number' &&
|
|
350
|
+
typeof event.stream === 'string' &&
|
|
351
|
+
typeof event.offset === 'number' &&
|
|
352
|
+
typeof event.length === 'number');
|
|
353
|
+
}
|
|
354
|
+
function isCompleteIndex(index, entries) {
|
|
355
|
+
if (!sameEntries(index.segments, entries))
|
|
356
|
+
return false;
|
|
357
|
+
let position = 1;
|
|
358
|
+
return index.segments.every((segment) => {
|
|
359
|
+
let offset = 0;
|
|
360
|
+
const complete = segment.startPosition === position &&
|
|
361
|
+
segment.count === segment.events.length &&
|
|
362
|
+
segment.events.every((event) => {
|
|
363
|
+
const matches = event.globalPosition === position &&
|
|
364
|
+
event.offset === offset &&
|
|
365
|
+
Number.isSafeInteger(event.length) &&
|
|
366
|
+
event.length > 0;
|
|
367
|
+
position += 1;
|
|
368
|
+
offset += event.length;
|
|
369
|
+
return matches;
|
|
370
|
+
}) &&
|
|
371
|
+
offset === segment.size;
|
|
372
|
+
return complete;
|
|
373
|
+
});
|
|
374
|
+
}
|
|
161
375
|
function sameEntries(a, b) {
|
|
162
376
|
return (a.length === b.length &&
|
|
163
377
|
a.every((entry, index) => {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
._shell_1rpcj_1{grid-template-rows:auto auto auto 1fr;min-height:100vh;display:grid}._header_1rpcj_6{align-items:center;gap:var(--space-2);padding:.55rem var(--space-4);background:var(--brand);color:var(--ink-inverse);display:flex}._brand_1rpcj_14{align-items:center;gap:var(--space-2);color:var(--ink-inverse);font-family:-apple-system,Segoe UI,Roboto,sans-serif;font-weight:700;font-size:var(--text-lg);letter-spacing:.01em;text-decoration:none;display:inline-flex}._brandLogo_1rpcj_25{width:1.75rem;height:1.75rem}._brandVersion_1rpcj_29{color:#ffffffb3;font-family:-apple-system,Segoe UI,Roboto,sans-serif;font-size:.78rem;font-weight:400}._statusBand_1rpcj_35{align-items:center;gap:var(--space-4);padding:.45rem var(--space-4);background:var(--brand-dark);font-size:var(--text-sm);color:var(--ink-inverse);border-top:1px solid #0000002e;flex-wrap:wrap;display:flex}._nav_1rpcj_46{padding:.4rem var(--space-4) 0;background:var(--brand-darker);scrollbar-width:none;-ms-overflow-style:none;border-bottom:1px solid var(--border);gap:.25rem;display:flex;overflow-x:auto}._nav_1rpcj_46::-webkit-scrollbar{display:none}._nav_1rpcj_46 a{color:var(--nav-ink-idle);white-space:nowrap;transition:color var(--motion-fast) ease;border-bottom:2px solid #0000;margin-bottom:-1px;padding:.4rem .7rem .45rem;text-decoration:none}._nav_1rpcj_46 a:hover{color:var(--ink-inverse);text-decoration:none}._nav_1rpcj_46 a[aria-current=page]{color:var(--accent-light);border-bottom-color:var(--accent);font-weight:700}._main_1rpcj_77{width:100%;padding:var(--space-6) var(--space-4);margin:0}._pageHeader_1rpcj_82{justify-content:space-between;align-items:end;gap:var(--space-4);margin-bottom:var(--space-5);display:flex}._pageHeader_1rpcj_82 h1{margin:0}._panel_1rpcj_92{background:var(--surface-panel);border:1px solid var(--border);border-radius:var(--radius);padding:var(--space-4);box-shadow:var(--shadow)}._badge_1rpcj_99{background:var(--surface-card);color:var(--ink);border-radius:999px;align-items:center;gap:.4rem;padding:.2rem .55rem;font-size:.85rem;font-weight:700;display:inline-flex}._badge_1rpcj_99:before{content:"";background:currentColor;border-radius:50%;flex:none;width:.55rem;height:.55rem}._badge_1rpcj_99>span{align-items:center;gap:.3rem;display:inline-flex}._good_1rpcj_123{color:var(--good)}._warning_1rpcj_126{color:var(--warning)}._bad_1rpcj_99{color:var(--bad)}._cold_1rpcj_132{color:#7dd3fc;background:#0e74902e;box-shadow:inset 0 0 0 1px #7dd3fc59}._button_1rpcj_137{appearance:none;border:1px solid var(--border-strong);border-radius:var(--radius-sm);background:var(--surface-card);color:var(--ink);font:inherit;font-size:var(--text-sm);cursor:pointer;padding:.22rem .55rem;font-weight:600}._button_1rpcj_137:hover:not(:disabled){border-color:var(--accent-light);color:var(--accent-light)}._button_1rpcj_137:disabled{opacity:.62;cursor:wait}._secondary_1rpcj_157{background:0 0}._tableWrap_1rpcj_160{overflow-x:auto}._table_1rpcj_160{border-collapse:collapse;width:100%;font-size:var(--text-sm)}._table_1rpcj_160 caption{text-align:left;font-weight:700;font-size:var(--text-sm);padding-bottom:.5rem}._table_1rpcj_160 th,._table_1rpcj_160 td{text-align:left;border-bottom:1px solid var(--border);vertical-align:top;padding:.25rem .45rem;line-height:1.3}._table_1rpcj_160 th{color:var(--ink-muted);font-weight:600;font-size:var(--text-xs);text-transform:uppercase;letter-spacing:.03em}._state_1rpcj_189{border:1px dashed var(--border);border-radius:var(--radius);padding:var(--space-5);color:var(--ink-muted)}._error_1rpcj_195{border-color:var(--bad);color:var(--bad)}._stale_1rpcj_199{color:var(--warning);font-size:.85rem}._statusActions_1rpcj_203{align-items:center;gap:.6rem;display:flex}._tickButton_1rpcj_208{border:1px solid var(--accent);color:var(--accent-light);background:#2dd4bf24;font-weight:700}._tickButton_1rpcj_208:hover:not(:disabled){border-color:var(--accent-light);background:#2dd4bf3d}._pauseControl_1rpcj_218{border:1px solid var(--bad);color:var(--warning);border-radius:var(--radius-sm);font-size:var(--text-sm);background:#dc262629;align-items:center;gap:.4rem;padding:.2rem .55rem;font-weight:700;display:inline-flex}._pauseControl_1rpcj_218 button{appearance:none;border:1px solid var(--bad);border-radius:var(--radius-sm);color:inherit;font:inherit;font-size:var(--text-xs);cursor:pointer;background:0 0;padding:.1rem .4rem;font-weight:700}._pauseControl_1rpcj_218 button:hover:not(:disabled){background:#dc26263d}._pauseControl_1rpcj_218 button:disabled{opacity:.62;cursor:wait}._pagination_1rpcj_249{justify-content:flex-end;gap:.6rem;margin-block:1rem;display:flex}._live_1rpcj_255{min-height:1.4rem}._header_1rpcj_6 ._live_1rpcj_255{color:var(--warning)}._json_1rpcj_261{background:var(--surface-inset);color:var(--ink);padding:var(--space-3);border-radius:var(--radius-sm);font-family:var(--font-mono);font-size:.75rem;overflow:auto}._chip_1rpcj_270{background:var(--border);color:var(--ink);border-radius:var(--radius-sm);font-size:var(--text-xs);align-items:center;gap:.25rem;margin-right:.2rem;padding:.05rem .35rem;display:inline-flex}._chipOutline_1rpcj_281{border:1px solid var(--border-strong);color:var(--ink-muted);background:0 0}._chipOutline_1rpcj_281._good_1rpcj_123{color:var(--good)}._chipOutline_1rpcj_281._warning_1rpcj_126{color:var(--warning)}._chipOutline_1rpcj_281._bad_1rpcj_99{color:var(--bad)}._chipOutline_1rpcj_281._cold_1rpcj_132{color:#7dd3fc}._tile_1rpcj_298{background:var(--surface-panel);border-radius:var(--radius);min-width:120px;padding:.6rem .9rem}._tileValue_1rpcj_304{font-size:1.3rem;font-weight:700}._tileLabel_1rpcj_308{color:var(--ink-muted);font-size:var(--text-xs);text-transform:uppercase}@media (width<=42rem){._header_1rpcj_6{padding-inline:var(--space-3)}._nav_1rpcj_46{padding-inline:var(--space-2)}._main_1rpcj_77{width:100%;padding:var(--space-4) var(--space-2) 0}._pageHeader_1rpcj_82{flex-direction:column;align-items:start}}._boardControls_a415m_1{justify-content:flex-end;margin-bottom:.75rem;display:flex}._boardControls_a415m_1 label{color:var(--ink-muted);font-size:var(--text-sm);align-items:center;gap:.4rem;font-weight:600;display:flex}._boardControls_a415m_1 select{background:var(--surface-panel);border:1px solid var(--border);border-radius:var(--radius-sm);min-height:2.1rem;color:var(--ink);font:inherit;padding-inline:.5rem}._boardControls_a415m_1 select:focus{border-color:var(--accent);outline:none;box-shadow:0 0 0 3px #2dd4bf26}._swimlanes_a415m_28{gap:1rem;display:grid}._swimlaneHeader_a415m_32{font-size:var(--text-sm);text-transform:uppercase;letter-spacing:.04em;color:var(--ink-muted);margin:0 0 .4rem;font-weight:700}._swimlaneToggle_a415m_40{justify-content:space-between;align-items:center;gap:var(--space-2);width:100%;color:inherit;cursor:pointer;font:inherit;text-align:left;background:0 0;border:0;padding:0;display:flex}._swimlaneToggle_a415m_40:hover,._swimlaneToggle_a415m_40:focus-visible{color:var(--accent-light)}._swimlaneToggle_a415m_40:focus-visible{outline:2px solid var(--focus-ring);outline-offset:2px}._board_a415m_1{grid-template-columns:repeat(5,minmax(0,1fr));align-items:stretch;gap:.6rem;display:grid;overflow-x:auto}._column_a415m_69{background:var(--surface-panel);border-radius:var(--radius);flex-direction:column;min-height:200px;padding:.5rem;display:flex}._columnHeader_a415m_77{justify-content:space-between;align-items:center;gap:var(--space-2);margin:.2rem .4rem .5rem;display:flex}._column_a415m_69 h2{font-size:var(--text-xs);text-transform:uppercase;letter-spacing:.04em;color:var(--ink-muted);margin:0}._cards_a415m_91{gap:.5rem;margin:0;padding:0;list-style:none;display:grid}._card_a415m_91{background:var(--surface-card);border:1px solid var(--border);border-left:3px solid var(--border-strong);font-size:var(--text-sm);border-radius:.5rem;overflow:hidden}._card_a415m_91:hover,._card_a415m_91:focus-within{border-color:var(--accent)}._cardLink_a415m_110{min-height:100%;color:var(--ink);transition:background var(--motion-fast) ease;padding:.5rem;text-decoration:none;display:block}._cardLink_a415m_110:hover{background:var(--surface-inset);text-decoration:none}._cardLink_a415m_110:focus-visible{outline:2px solid var(--focus-ring);outline-offset:-2px}._cardRef_a415m_126{color:var(--ink-muted);font-family:var(--font-mono);font-size:var(--text-xs);margin-bottom:.15rem;display:block}._cardTitle_a415m_133{color:var(--ink);margin-bottom:.25rem;font-weight:600;display:block}._cardMeta_a415m_139{flex-wrap:wrap;align-items:center;gap:.3rem;margin-top:.2rem;display:flex}._metaIcon_a415m_146{flex:none;width:.75rem;height:.75rem}._cardStats_a415m_151{color:var(--ink-muted);margin-top:.3rem;font-size:.7rem}._childRun_a415m_156{border-radius:var(--radius-sm);background:var(--surface-inset);border:1px solid var(--border);color:var(--ink-muted);font-size:var(--text-xs);grid-template-columns:auto 1fr;align-items:center;gap:.25rem .45rem;margin-top:.45rem;padding:.4rem;display:grid}._childRunDot_a415m_169{background:var(--accent);border-radius:50%;width:.48rem;height:.48rem;box-shadow:0 0 0 3px #2dd4bf24}._childRunTitle_a415m_176{color:var(--ink);font-weight:650}._childRunMeta_a415m_180{color:var(--ink-muted)}._columnToggle_a415m_183{border:1px solid var(--border-strong);border-radius:var(--radius-sm);width:1.65rem;height:1.65rem;color:var(--ink-muted);cursor:pointer;font-size:var(--text-base);background:0 0;flex:none;justify-content:center;align-items:center;line-height:1;display:none}._finishedColumnToggle_a415m_198{display:inline-flex}._columnToggle_a415m_183:hover{border-color:var(--accent);color:var(--accent-light)}._filters_a415m_205{flex-wrap:wrap;align-items:end;gap:.6rem;margin-bottom:1rem;display:flex}._filters_a415m_205 label{color:var(--ink-muted);font-weight:600;font-size:var(--text-sm);gap:.25rem;display:grid}._filters_a415m_205 input,._filters_a415m_205 select{background:var(--surface-panel);border:1px solid var(--border);border-radius:var(--radius-sm);min-height:2.35rem;color:var(--ink);font:inherit;padding-inline:.55rem}._filters_a415m_205 input:focus,._filters_a415m_205 select:focus{border-color:var(--accent);outline:none;box-shadow:0 0 0 3px #2dd4bf26}._detail_a415m_235{gap:var(--space-3);display:grid}._detail_a415m_235>h2{margin:0}._summary_a415m_242{font-size:var(--text-sm);grid-template-columns:max-content 1fr;align-items:baseline;gap:.35rem .7rem;margin:0;display:grid}._summary_a415m_242 dt{color:var(--ink-muted);margin:0}._summary_a415m_242 dd{overflow-wrap:anywhere;min-width:0;margin:0}._overviewLayout_a415m_259{gap:var(--space-5);grid-template-columns:minmax(0,2fr) minmax(0,1fr);align-items:start;display:grid}._overviewSidebar_a415m_265{gap:var(--space-3);grid-column:2;min-width:0;display:grid}._overviewMain_a415m_271{grid-area:1/1;min-width:0}._sidebarSectionTitle_a415m_276{font-size:var(--text-sm)}._resourceList_a415m_279{margin:0 0 var(--space-4);gap:var(--space-2);flex-direction:column;padding:0;list-style:none;display:flex}._resourceCard_a415m_287{padding:var(--space-2) var(--space-3);border:1px solid var(--border);border-radius:var(--radius-sm);background:var(--surface-card);color:inherit;flex-direction:column;gap:.35rem;text-decoration:none;display:flex}a._resourceCard_a415m_287:hover{border-color:var(--accent);text-decoration:none}._resourceCardTop_a415m_302{align-items:center;gap:var(--space-2);display:flex}._resourceCardIcon_a415m_307{color:var(--ink-muted);flex:none}._resourceCardTitle_a415m_311{min-width:0;font-weight:600;font-size:var(--text-sm);text-overflow:ellipsis;white-space:nowrap;flex:auto;overflow:hidden}._resourceCardExt_a415m_320{color:var(--ink-muted);opacity:0;flex:none}._resourceCard_a415m_287:hover ._resourceCardExt_a415m_320{opacity:1}._resourceCardMeta_a415m_328{font-size:var(--text-sm);padding-left:calc(18px + var(--space-2));flex-wrap:wrap;align-items:baseline;gap:.4rem;display:flex}._resourceId_a415m_336{color:var(--ink-muted);font-family:var(--font-mono);font-size:var(--text-xs);overflow-wrap:anywhere}._eventList_a415m_342{flex-direction:column;gap:.45rem;margin:1rem 0;padding:0;list-style:none;display:flex}._event_a415m_342{background:var(--surface-card);border:1px solid var(--border);border-radius:var(--radius-sm);min-height:2.25rem;overflow:hidden}._event_a415m_342:hover{border-color:var(--border-strong)}._event_a415m_342 time{color:var(--ink-muted);font-family:var(--font-mono);font-size:var(--text-xs)}._eventType_a415m_365{font-weight:650;font-size:var(--text-sm);text-overflow:ellipsis;white-space:nowrap;min-width:0;overflow:hidden}._eventId_a415m_373{color:var(--ink-muted);font-family:var(--font-mono);font-size:var(--text-xs);text-overflow:ellipsis;white-space:nowrap;min-width:0;overflow:hidden}._toolbar_a415m_382{flex-wrap:wrap;align-items:center;gap:.7rem;display:flex}._modalBackdrop_a415m_388{background:var(--brand-darker);padding:var(--space-4);z-index:10;place-items:center;display:grid;position:fixed;inset:0}._modal_a415m_388{background:var(--surface-panel);border:1px solid var(--border-strong);box-shadow:var(--shadow);color:var(--ink);border-radius:var(--radius);width:100%;max-width:none;height:100%;max-height:none;padding:var(--space-4);overflow:auto}._modalHeader_a415m_411{justify-content:space-between;align-items:center;gap:var(--space-3);margin-bottom:var(--space-3);display:flex}._modalRef_a415m_418{color:var(--ink-muted);font-family:var(--font-mono);font-size:var(--text-sm)}._modalClose_a415m_423{appearance:none;border:1px solid var(--border-strong);border-radius:var(--radius-sm);background:var(--surface-card);color:var(--ink);cursor:pointer;font:inherit;font-size:var(--text-sm);padding:.35rem .7rem;font-weight:700}._modalClose_a415m_423:hover{border-color:var(--accent-light);color:var(--accent-light)}._tiles_a415m_439{margin-bottom:var(--space-4);flex-wrap:wrap;gap:.6rem;display:flex}@media (width<=42rem){._overviewLayout_a415m_259{grid-template-columns:minmax(0,1fr)}._overviewSidebar_a415m_265,._overviewMain_a415m_271{grid-area:auto/1}._board_a415m_1{display:block;overflow:visible}._column_a415m_69{min-height:unset;margin-bottom:.75rem}._columnToggle_a415m_183{display:inline-flex}._eventId_a415m_373{display:none}._modalBackdrop_a415m_388{background:0 0;padding:0;display:block;position:static}._modal_a415m_388{box-shadow:none;border:0;width:auto;height:auto;max-height:none;padding:0}}@media (prefers-reduced-motion:reduce){*{scroll-behavior:auto!important;transition-duration:.01ms!important;animation-duration:.01ms!important}}._transcript_a415m_490{margin:var(--space-3) 0 0;padding:0;list-style:none}._transcriptEntry_a415m_495{background:var(--surface-inset);border:1px solid var(--border);border-radius:var(--radius-sm);margin-bottom:.75rem;overflow:hidden}._transcriptHead_a415m_502{gap:var(--space-3);color:var(--ink-muted);background:var(--surface-panel);border-bottom:1px solid var(--border);font-family:var(--font-mono);font-size:var(--text-xs);padding:.45rem .6rem;display:flex}._transcriptText_a415m_512{white-space:pre-wrap;font-family:var(--font-mono);background:0 0;margin:0;padding:.6rem;font-size:.76rem;overflow-x:auto}._transcriptLayout_a415m_521{gap:var(--space-4);margin-top:var(--space-3);grid-template-columns:minmax(15rem,22rem) minmax(0,1fr);display:grid}._transcriptGroups_a415m_527{align-content:start;align-self:start;gap:var(--space-2);margin:0;padding:0;list-style:none;display:grid}._transcriptGroup_a415m_527{width:100%;padding:var(--space-2);color:var(--ink);text-align:left;background:var(--surface-card);border:1px solid var(--border);border-radius:var(--radius-sm);cursor:pointer;font:inherit;font-size:var(--text-xs);overflow-wrap:anywhere;gap:.2rem;display:grid}._transcriptGroup_a415m_527:hover,._transcriptGroup_a415m_527[aria-pressed=true]{border-color:var(--accent);background:var(--surface-inset)}._transcriptConversation_a415m_556{min-width:0}._transcriptFilters_a415m_559{align-items:center;gap:var(--space-3);flex-wrap:wrap;display:flex}._transcriptFilters_a415m_559 label{align-items:center;gap:var(--space-2);color:var(--ink-muted);font-size:var(--text-sm);display:flex}._transcriptFilters_a415m_559 select{color:var(--ink);background:var(--surface-panel);border:1px solid var(--border);border-radius:var(--radius-sm);font:inherit}._transcriptInput_a415m_579,._transcriptAgent_a415m_580{border:1px solid var(--border);border-radius:var(--radius-sm);overflow:hidden}._transcriptInput_a415m_579{background:var(--surface-inset);border-left:3px solid var(--accent);margin-left:12%}._transcriptAgent_a415m_580{background:var(--surface-card);border-left:3px solid var(--border-strong);margin-right:12%}._transcriptRunSeparator_a415m_595{align-items:center;gap:var(--space-2);margin:var(--space-3) 0;color:var(--ink-muted);font-family:var(--font-mono);font-size:var(--text-xs);display:flex}._transcriptRunSeparator_a415m_595:before,._transcriptRunSeparator_a415m_595:after{content:"";background:var(--border);flex:1;height:1px}._eventButton_a415m_612{width:100%;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;grid-template-columns:minmax(11rem,14rem) minmax(0,1fr) minmax(8rem,18rem) 2.4rem;align-items:center;gap:.65rem;padding:.45rem .65rem;display:grid}._eventType_a415m_365{text-overflow:clip;white-space:normal;overflow-wrap:anywhere;overflow:visible}@media (width<=42rem){._transcriptLayout_a415m_521{grid-template-columns:minmax(0,1fr)}._transcriptInput_a415m_579,._transcriptAgent_a415m_580{margin-inline:0}._eventButton_a415m_612{grid-template-rows:auto auto;grid-template-columns:minmax(0,1fr) 2.2rem}._eventType_a415m_365{text-overflow:ellipsis;white-space:nowrap;overflow-wrap:normal;grid-area:2/1/auto/-1;overflow:hidden}}._eventChevron_a415m_652{color:var(--ink-muted);justify-self:end}._eventPayload_a415m_656{border-top:1px solid var(--border);overflow-x:auto}._eventPayload_a415m_656 pre{padding:var(--space-3);white-space:pre;margin:0}._tabs_a415m_665{padding:.4rem var(--space-4) 0;border-bottom:1px solid var(--border);background:0 0;gap:.25rem;margin:0;display:flex}._tabs_a415m_665 button{color:var(--nav-ink-idle);font:inherit;white-space:nowrap;cursor:pointer;transition:color var(--motion-fast) ease;background:0 0;border:0;border-bottom:2px solid #0000;margin-bottom:-1px;padding:.4rem .7rem .45rem}._tabs_a415m_665 button:hover{color:var(--ink-inverse)}._tabs_a415m_665 button[aria-selected=true]{color:var(--accent-light);border-bottom-color:var(--accent);font-weight:700}._actionBar_a415m_694{gap:var(--space-2);flex-wrap:wrap;align-items:center;display:flex}._dangerButton_a415m_700{border-color:var(--bad);color:var(--bad)}:root{--wake-ink-950:#14161a;--wake-ink-900:#1a1d23;--wake-ink-850:#22262e;--wake-ink-800:#101216;--wake-ink-700:#2c313a;--wake-ink-600:#3a4150;--wake-grey-300:#cbd5e1;--wake-grey-400:#9aa2ad;--wake-grey-100:#e8e8e8;--wake-white:#fff;--wake-teal-700:#103a37;--wake-teal-650:#134e4a;--wake-teal-600:#0f766e;--wake-mint-400:#2dd4bf;--wake-mint-300:#5eead4;--wake-green-300:#7fe3a3;--wake-green-900:#1f3d2c;--wake-blue-300:#7fb3ff;--wake-blue-900:#1f3350;--wake-purple-300:#c79bff;--wake-purple-900:#2d1f50;--wake-amber-300:#ffcf7f;--wake-amber-900:#4a3510;--wake-red-300:#ff8f7f;--wake-red-900:#3d1f1f;--wake-slate-400:#9aa2ad;--wake-slate-900:#252830;--surface:var(--wake-ink-950);--surface-panel:var(--wake-ink-900);--surface-card:var(--wake-ink-850);--surface-inset:var(--wake-ink-800);--border:var(--wake-ink-700);--border-strong:var(--wake-ink-600);--ink:var(--wake-grey-100);--ink-muted:var(--wake-grey-400);--ink-inverse:var(--wake-white);--brand:var(--wake-teal-600);--brand-dark:var(--wake-teal-650);--brand-darker:var(--wake-teal-700);--nav-ink-idle:var(--wake-grey-300);--accent:var(--wake-mint-400);--accent-light:var(--wake-mint-300);--good:var(--wake-green-300);--warning:var(--wake-amber-300);--bad:var(--wake-red-300);--cond-ready-fg:var(--wake-green-300);--cond-ready-bg:var(--wake-green-900);--cond-scheduled-fg:var(--wake-blue-300);--cond-scheduled-bg:var(--wake-blue-900);--cond-active-fg:var(--wake-purple-300);--cond-active-bg:var(--wake-purple-900);--cond-needs-input-fg:var(--wake-amber-300);--cond-needs-input-bg:var(--wake-amber-900);--cond-error-fg:var(--wake-red-300);--cond-error-bg:var(--wake-red-900);--cond-finished-fg:var(--wake-slate-400);--cond-finished-bg:var(--wake-slate-900);--space-2:.5rem;--space-3:.75rem;--space-4:1rem;--space-5:1.5rem;--space-6:2rem;--radius:.65rem;--radius-sm:.4rem;--shadow:0 1px 3px #00000059;--focus-ring:var(--wake-mint-300);--font-sans:Inter, ui-sans-serif, system-ui, sans-serif;--font-mono:ui-monospace, SFMono-Regular, Consolas, monospace;--text-xs:.72rem;--text-sm:.8rem;--text-base:1rem;--text-lg:1.15rem;--content-width:76rem;--motion-fast:.12s}*{box-sizing:border-box}html{color:var(--ink);background:var(--surface);font-family:var(--font-sans);line-height:1.5}body{margin:0}a{color:var(--accent-light);text-underline-offset:.18em;text-decoration:none}a:hover{text-decoration:underline}button,input,select{font:inherit}:focus-visible{outline:3px solid var(--focus-ring);outline-offset:3px}@media (prefers-reduced-motion:reduce){html{scroll-behavior:auto}}
|