crewly 1.20.306 → 1.20.307
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/config/hooks/agent-status/report.sh +39 -1
- package/dist/backend/backend/src/constants.d.ts +54 -0
- package/dist/backend/backend/src/constants.d.ts.map +1 -1
- package/dist/backend/backend/src/constants.js +54 -0
- package/dist/backend/backend/src/constants.js.map +1 -1
- package/dist/backend/backend/src/controllers/agent-hooks/agent-hooks.controller.d.ts +2 -1
- package/dist/backend/backend/src/controllers/agent-hooks/agent-hooks.controller.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/agent-hooks/agent-hooks.controller.js +18 -1
- package/dist/backend/backend/src/controllers/agent-hooks/agent-hooks.controller.js.map +1 -1
- package/dist/backend/backend/src/controllers/board/board.controller.d.ts +23 -0
- package/dist/backend/backend/src/controllers/board/board.controller.d.ts.map +1 -0
- package/dist/backend/backend/src/controllers/board/board.controller.js +48 -0
- package/dist/backend/backend/src/controllers/board/board.controller.js.map +1 -0
- package/dist/backend/backend/src/controllers/board/board.routes.d.ts +15 -0
- package/dist/backend/backend/src/controllers/board/board.routes.d.ts.map +1 -0
- package/dist/backend/backend/src/controllers/board/board.routes.js +21 -0
- package/dist/backend/backend/src/controllers/board/board.routes.js.map +1 -0
- package/dist/backend/backend/src/controllers/browser/browser.controller.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/browser/browser.controller.js +2 -0
- package/dist/backend/backend/src/controllers/browser/browser.controller.js.map +1 -1
- package/dist/backend/backend/src/controllers/chat/chat.controller.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/chat/chat.controller.js +5 -0
- package/dist/backend/backend/src/controllers/chat/chat.controller.js.map +1 -1
- package/dist/backend/backend/src/controllers/slack/slack.controller.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/slack/slack.controller.js +3 -0
- package/dist/backend/backend/src/controllers/slack/slack.controller.js.map +1 -1
- package/dist/backend/backend/src/index.d.ts +9 -0
- package/dist/backend/backend/src/index.d.ts.map +1 -1
- package/dist/backend/backend/src/index.js +46 -0
- package/dist/backend/backend/src/index.js.map +1 -1
- package/dist/backend/backend/src/routes/api.routes.d.ts.map +1 -1
- package/dist/backend/backend/src/routes/api.routes.js +3 -0
- package/dist/backend/backend/src/routes/api.routes.js.map +1 -1
- package/dist/backend/backend/src/services/board/board-activity.normalizer.d.ts +85 -0
- package/dist/backend/backend/src/services/board/board-activity.normalizer.d.ts.map +1 -0
- package/dist/backend/backend/src/services/board/board-activity.normalizer.js +241 -0
- package/dist/backend/backend/src/services/board/board-activity.normalizer.js.map +1 -0
- package/dist/backend/backend/src/services/board/board-activity.tracker.d.ts +154 -0
- package/dist/backend/backend/src/services/board/board-activity.tracker.d.ts.map +1 -0
- package/dist/backend/backend/src/services/board/board-activity.tracker.js +322 -0
- package/dist/backend/backend/src/services/board/board-activity.tracker.js.map +1 -0
- package/dist/backend/backend/src/services/board/board-outcome-phrases.d.ts +43 -0
- package/dist/backend/backend/src/services/board/board-outcome-phrases.d.ts.map +1 -0
- package/dist/backend/backend/src/services/board/board-outcome-phrases.js +76 -0
- package/dist/backend/backend/src/services/board/board-outcome-phrases.js.map +1 -0
- package/dist/backend/backend/src/services/board/board-push.service.d.ts +114 -0
- package/dist/backend/backend/src/services/board/board-push.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/board/board-push.service.js +312 -0
- package/dist/backend/backend/src/services/board/board-push.service.js.map +1 -0
- package/dist/backend/backend/src/services/board/board-redact.d.ts +90 -0
- package/dist/backend/backend/src/services/board/board-redact.d.ts.map +1 -0
- package/dist/backend/backend/src/services/board/board-redact.js +359 -0
- package/dist/backend/backend/src/services/board/board-redact.js.map +1 -0
- package/dist/backend/backend/src/services/board/board-snapshot.builder.d.ts +176 -0
- package/dist/backend/backend/src/services/board/board-snapshot.builder.d.ts.map +1 -0
- package/dist/backend/backend/src/services/board/board-snapshot.builder.js +342 -0
- package/dist/backend/backend/src/services/board/board-snapshot.builder.js.map +1 -0
- package/dist/backend/backend/src/services/board/board-usage.d.ts +20 -0
- package/dist/backend/backend/src/services/board/board-usage.d.ts.map +1 -0
- package/dist/backend/backend/src/services/board/board-usage.js +82 -0
- package/dist/backend/backend/src/services/board/board-usage.js.map +1 -0
- package/dist/backend/backend/src/services/board/board.service.d.ts +60 -0
- package/dist/backend/backend/src/services/board/board.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/board/board.service.js +82 -0
- package/dist/backend/backend/src/services/board/board.service.js.map +1 -0
- package/dist/backend/backend/src/services/board/board.sources.d.ts +25 -0
- package/dist/backend/backend/src/services/board/board.sources.d.ts.map +1 -0
- package/dist/backend/backend/src/services/board/board.sources.js +293 -0
- package/dist/backend/backend/src/services/board/board.sources.js.map +1 -0
- package/dist/backend/backend/src/services/board/board.types.d.ts +117 -0
- package/dist/backend/backend/src/services/board/board.types.d.ts.map +1 -0
- package/dist/backend/backend/src/services/board/board.types.js +8 -0
- package/dist/backend/backend/src/services/board/board.types.js.map +1 -0
- package/dist/backend/backend/src/services/google/google-reauth-notifier.service.d.ts +13 -0
- package/dist/backend/backend/src/services/google/google-reauth-notifier.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/google/google-reauth-notifier.service.js +15 -0
- package/dist/backend/backend/src/services/google/google-reauth-notifier.service.js.map +1 -1
- package/dist/backend/build-info.json +2 -2
- package/dist/cli/backend/src/constants.d.ts +54 -0
- package/dist/cli/backend/src/constants.d.ts.map +1 -1
- package/dist/cli/backend/src/constants.js +54 -0
- package/dist/cli/backend/src/constants.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Board snapshot builder (crewly#1247): turns what the machine knows (teams,
|
|
3
|
+
* runtime turn state, WorkItems, tickets, decisions, the usage ledger and the
|
|
4
|
+
* live activity tracker) into the {@link BoardSnapshot} Cloud and the local
|
|
5
|
+
* dashboard show.
|
|
6
|
+
*
|
|
7
|
+
* Everything is read through {@link BoardSources}, so the builder is a pure
|
|
8
|
+
* function of its inputs and is tested with fixtures. A full build refreshes
|
|
9
|
+
* a cache (roster, usage, tasks, owner waits); the per-agent delta rows are
|
|
10
|
+
* built from that cache plus the live activity, so a delta never rescans the
|
|
11
|
+
* disk.
|
|
12
|
+
*
|
|
13
|
+
* Every text in the result went through {@link scrubText}.
|
|
14
|
+
*
|
|
15
|
+
* @module services/board/board-snapshot.builder
|
|
16
|
+
*/
|
|
17
|
+
import { BOARD_CONSTANTS } from '../../constants.js';
|
|
18
|
+
import { scrubText } from './board-redact.js';
|
|
19
|
+
import { resultOf, summarizeResults } from './board-outcome-phrases.js';
|
|
20
|
+
const C = BOARD_CONSTANTS;
|
|
21
|
+
/** Local midnight of `now`. */
|
|
22
|
+
export function startOfLocalDay(now) {
|
|
23
|
+
const d = new Date(now);
|
|
24
|
+
d.setHours(0, 0, 0, 0);
|
|
25
|
+
return d.getTime();
|
|
26
|
+
}
|
|
27
|
+
const iso = (ms) => new Date(ms).toISOString();
|
|
28
|
+
const msOf = (s) => (s ? Date.parse(s) : NaN);
|
|
29
|
+
/** Hours/minutes in words for stuck reasons. */
|
|
30
|
+
function age(ms) {
|
|
31
|
+
const m = Math.max(1, Math.round(ms / 60_000));
|
|
32
|
+
if (m < 60)
|
|
33
|
+
return `${m} min`;
|
|
34
|
+
const h = Math.round(m / 60);
|
|
35
|
+
return h < 48 ? `${h} h` : `${Math.round(h / 24)} days`;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Builds board snapshots and per-agent rows.
|
|
39
|
+
*/
|
|
40
|
+
export class BoardSnapshotBuilder {
|
|
41
|
+
sources;
|
|
42
|
+
tracker;
|
|
43
|
+
clock;
|
|
44
|
+
cache = { roster: new Map(), usage: null, tasks: new Map(), waits: new Map() };
|
|
45
|
+
/**
|
|
46
|
+
* @param sources - Everything the board reads
|
|
47
|
+
* @param tracker - Live activity
|
|
48
|
+
* @param clock - Clock (tests)
|
|
49
|
+
*/
|
|
50
|
+
constructor(sources, tracker, clock = Date.now) {
|
|
51
|
+
this.sources = sources;
|
|
52
|
+
this.tracker = tracker;
|
|
53
|
+
this.clock = clock;
|
|
54
|
+
}
|
|
55
|
+
/** Whether a full build has filled the cache. */
|
|
56
|
+
get primed() {
|
|
57
|
+
return this.cache.roster.size > 0;
|
|
58
|
+
}
|
|
59
|
+
/** This machine's identity. */
|
|
60
|
+
identity() {
|
|
61
|
+
return this.sources.identity();
|
|
62
|
+
}
|
|
63
|
+
/** Sessions known from the last full build. */
|
|
64
|
+
knownSessions() {
|
|
65
|
+
return [...this.cache.roster.keys()];
|
|
66
|
+
}
|
|
67
|
+
async safe(read, fallback) {
|
|
68
|
+
try {
|
|
69
|
+
return await read();
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
return fallback;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Build the full snapshot and refresh the cache.
|
|
77
|
+
*
|
|
78
|
+
* @returns The snapshot
|
|
79
|
+
*/
|
|
80
|
+
async build() {
|
|
81
|
+
const now = this.clock();
|
|
82
|
+
const dayStart = startOfLocalDay(now);
|
|
83
|
+
const [identity, version, teams, workItems, tickets, decisions, holds, usage, active] = await Promise.all([
|
|
84
|
+
this.sources.identity(),
|
|
85
|
+
this.safe(() => this.sources.version(), 'unknown'),
|
|
86
|
+
this.safe(() => this.sources.teams(), []),
|
|
87
|
+
this.safe(() => this.sources.workItems(), []),
|
|
88
|
+
this.safe(() => this.sources.tickets(), []),
|
|
89
|
+
this.safe(() => this.sources.decisions(), []),
|
|
90
|
+
this.safe(() => this.sources.holds(), []),
|
|
91
|
+
this.safe(() => this.sources.usage(now), null),
|
|
92
|
+
this.safe(() => this.sources.activeWork(), new Map()),
|
|
93
|
+
]);
|
|
94
|
+
const roster = new Map();
|
|
95
|
+
for (const team of teams)
|
|
96
|
+
for (const member of team.members)
|
|
97
|
+
roster.set(member.session, { team, member });
|
|
98
|
+
const waits = new Map();
|
|
99
|
+
for (const d of decisions)
|
|
100
|
+
if (d.asker && !waits.has(d.asker))
|
|
101
|
+
waits.set(d.asker, d);
|
|
102
|
+
const tasks = new Map(usage?.taskOf ?? []);
|
|
103
|
+
for (const [session, task] of active)
|
|
104
|
+
tasks.set(session, task);
|
|
105
|
+
this.cache = { roster, usage, tasks, waits };
|
|
106
|
+
const teamName = (id, agent) => {
|
|
107
|
+
const byId = id ? teams.find((t) => t.teamId === id)?.name : undefined;
|
|
108
|
+
return byId ?? (agent ? roster.get(agent)?.team.name : undefined);
|
|
109
|
+
};
|
|
110
|
+
const agentName = (session) => (session ? (roster.get(session)?.member.name ?? undefined) : undefined);
|
|
111
|
+
return {
|
|
112
|
+
v: 1,
|
|
113
|
+
instanceId: identity.instanceId,
|
|
114
|
+
machineName: scrubText(identity.machineName, 60) || 'This machine',
|
|
115
|
+
crewlyVersion: scrubText(version, 30) || 'unknown',
|
|
116
|
+
generatedAt: iso(now),
|
|
117
|
+
outcomes: {
|
|
118
|
+
...this.doneToday(tickets, workItems, dayStart, teamName, agentName),
|
|
119
|
+
waitingOnOwner: this.waiting(decisions, holds, tickets, teamName, agentName),
|
|
120
|
+
stuck: this.stuck(workItems, tickets, roster, now, teamName, agentName),
|
|
121
|
+
spend: usage?.spend ?? { todayUsd: 0, weekUsd: 0, todayTokens: 0, weekTokens: 0 },
|
|
122
|
+
},
|
|
123
|
+
teams: teams.map((t) => ({
|
|
124
|
+
teamId: t.teamId,
|
|
125
|
+
name: scrubText(t.name, 60) || t.teamId,
|
|
126
|
+
...(t.paused ? { paused: true } : {}),
|
|
127
|
+
agents: t.members.map((m) => this.row(m, now)),
|
|
128
|
+
})),
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Rows for some agents, from the cache and the live activity. No disk or
|
|
133
|
+
* network reads.
|
|
134
|
+
*
|
|
135
|
+
* @param sessions - Sessions to build
|
|
136
|
+
* @returns Rows of the known sessions
|
|
137
|
+
*/
|
|
138
|
+
rows(sessions) {
|
|
139
|
+
const now = this.clock();
|
|
140
|
+
const out = [];
|
|
141
|
+
for (const session of new Set(sessions)) {
|
|
142
|
+
const entry = this.cache.roster.get(session);
|
|
143
|
+
if (!entry)
|
|
144
|
+
continue;
|
|
145
|
+
out.push({ ...this.row(entry.member, now), teamId: entry.team.teamId });
|
|
146
|
+
}
|
|
147
|
+
return out;
|
|
148
|
+
}
|
|
149
|
+
// -------------------------------------------------------------------------
|
|
150
|
+
// Agent rows
|
|
151
|
+
// -------------------------------------------------------------------------
|
|
152
|
+
row(m, now) {
|
|
153
|
+
const turn = this.safeSync(() => this.sources.turnState(m.session, now), 'unknown');
|
|
154
|
+
const working = turn === 'working' || (turn === 'unknown' && m.workingStatus === 'in_progress' && m.agentStatus !== 'inactive');
|
|
155
|
+
const decision = this.cache.waits.get(m.session);
|
|
156
|
+
const hookWaiting = this.safeSync(() => this.sources.waitingOnHuman(m.session), false);
|
|
157
|
+
const error = this.safeSync(() => this.sources.errorOf?.(m.session) ?? null, null);
|
|
158
|
+
const queued = this.safeSync(() => this.sources.queuedFor(m.session), 0);
|
|
159
|
+
const stopped = m.agentStatus === 'inactive' || m.agentStatus === 'suspended';
|
|
160
|
+
const live = this.tracker.live(m.session, working && !stopped, now);
|
|
161
|
+
const trackerWaiting = live.activity?.type === 'waiting_owner';
|
|
162
|
+
let state;
|
|
163
|
+
if (stopped)
|
|
164
|
+
state = 'stopped';
|
|
165
|
+
else if (error)
|
|
166
|
+
state = 'error';
|
|
167
|
+
else if (hookWaiting || trackerWaiting || (decision && !working))
|
|
168
|
+
state = 'waiting';
|
|
169
|
+
else if (working)
|
|
170
|
+
state = 'working';
|
|
171
|
+
else if (queued > 0)
|
|
172
|
+
state = 'queued';
|
|
173
|
+
else
|
|
174
|
+
state = 'idle';
|
|
175
|
+
let activity = live.activity ?? undefined;
|
|
176
|
+
if (state === 'waiting' && activity?.type !== 'waiting_owner') {
|
|
177
|
+
const what = decision ? scrubText(decision.title, 100) : 'a reply to its prompt';
|
|
178
|
+
activity = { type: 'waiting_owner', detail: `Waiting for the owner: ${what}`.slice(0, C.MAX_TEXT), since: decision?.createdAt ?? iso(now) };
|
|
179
|
+
}
|
|
180
|
+
if (stopped || state === 'idle' || state === 'queued')
|
|
181
|
+
activity = undefined;
|
|
182
|
+
if (state === 'error' && error)
|
|
183
|
+
activity = { type: 'other', detail: scrubText(error), since: iso(now) };
|
|
184
|
+
const task = this.cache.tasks.get(m.session);
|
|
185
|
+
const usage = this.cache.usage?.perAgent.get(m.session);
|
|
186
|
+
const row = {
|
|
187
|
+
session: m.session,
|
|
188
|
+
name: scrubText(m.name, 60) || m.session,
|
|
189
|
+
role: scrubText(m.role, 40),
|
|
190
|
+
runtime: scrubText(m.runtime, 30),
|
|
191
|
+
...(m.model ? { model: scrubText(m.model, 60) } : {}),
|
|
192
|
+
state,
|
|
193
|
+
...(activity ? { activity } : {}),
|
|
194
|
+
...(task && !stopped ? { task: { ...(task.id ? { id: task.id } : {}), title: scrubText(task.title), since: task.since } } : {}),
|
|
195
|
+
...(live.lastAction ? { lastAction: live.lastAction } : {}),
|
|
196
|
+
...(usage ? { todayUsd: round4(usage.todayUsd), todayTokens: Math.round(usage.todayTokens) } : {}),
|
|
197
|
+
};
|
|
198
|
+
return row;
|
|
199
|
+
}
|
|
200
|
+
safeSync(read, fallback) {
|
|
201
|
+
try {
|
|
202
|
+
return read();
|
|
203
|
+
}
|
|
204
|
+
catch {
|
|
205
|
+
return fallback;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
// -------------------------------------------------------------------------
|
|
209
|
+
// Outcomes
|
|
210
|
+
// -------------------------------------------------------------------------
|
|
211
|
+
doneToday(tickets, workItems, dayStart, teamName, agentName) {
|
|
212
|
+
const items = [];
|
|
213
|
+
for (const t of tickets) {
|
|
214
|
+
if (t.column !== 'done')
|
|
215
|
+
continue;
|
|
216
|
+
const at = msOf(t.completedAt ?? t.updatedAt);
|
|
217
|
+
if (!Number.isFinite(at) || at < dayStart)
|
|
218
|
+
continue;
|
|
219
|
+
const title = scrubText(t.title);
|
|
220
|
+
items.push({
|
|
221
|
+
ticketId: t.label ?? t.id,
|
|
222
|
+
title,
|
|
223
|
+
result: resultOf(title, t.kind),
|
|
224
|
+
team: teamName(t.teamId, t.assignee) ?? 'Crewly',
|
|
225
|
+
...(agentName(t.assignee) ? { agent: agentName(t.assignee) } : {}),
|
|
226
|
+
at: iso(at),
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
// WorkItems that belong to no ticket (a ticket carries its own result, and the
|
|
230
|
+
// harness's own verify / follow-through / retry items are bookkeeping, not results)
|
|
231
|
+
const OWNER_FACING_TYPES = new Set(['delegate', 'project_task', 'review']);
|
|
232
|
+
const BOOKKEEPING_TITLE = /^(?:verify|follow[- ]through|retry\b|refresh standing|tl fallback|re-?verify|\[?auto|[A-Z][A-Z0-9]{1,6}-\d+\b)/i;
|
|
233
|
+
for (const w of workItems) {
|
|
234
|
+
if (w.status !== 'verified' && w.status !== 'done')
|
|
235
|
+
continue;
|
|
236
|
+
if (!OWNER_FACING_TYPES.has(w.type))
|
|
237
|
+
continue;
|
|
238
|
+
if (w.requestId || w.projectTaskId)
|
|
239
|
+
continue;
|
|
240
|
+
if (BOOKKEEPING_TITLE.test(w.title.trim()))
|
|
241
|
+
continue;
|
|
242
|
+
const at = msOf(w.completedAt ?? w.statusChangedAt);
|
|
243
|
+
if (!Number.isFinite(at) || at < dayStart)
|
|
244
|
+
continue;
|
|
245
|
+
const title = scrubText(w.title);
|
|
246
|
+
items.push({
|
|
247
|
+
title,
|
|
248
|
+
result: resultOf(title),
|
|
249
|
+
team: teamName(undefined, w.target) ?? 'Crewly',
|
|
250
|
+
...(agentName(w.target) ? { agent: agentName(w.target) } : {}),
|
|
251
|
+
at: iso(at),
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
items.sort((a, b) => Date.parse(b.at) - Date.parse(a.at));
|
|
255
|
+
return { doneTodaySummary: summarizeResults(items.map((i) => i.result)), doneToday: items.slice(0, C.MAX_DONE) };
|
|
256
|
+
}
|
|
257
|
+
waiting(decisions, holds, tickets, teamName, agentName) {
|
|
258
|
+
const out = [];
|
|
259
|
+
for (const d of decisions) {
|
|
260
|
+
out.push({
|
|
261
|
+
id: `decision:${d.id}`,
|
|
262
|
+
kind: d.kind,
|
|
263
|
+
title: scrubText(d.title) || 'A decision is waiting',
|
|
264
|
+
...(teamName(d.teamId, d.asker) ? { team: teamName(d.teamId, d.asker) } : {}),
|
|
265
|
+
...(agentName(d.asker) ? { agent: agentName(d.asker) } : {}),
|
|
266
|
+
since: d.createdAt,
|
|
267
|
+
action: { label: d.kind === 'approval' ? 'Approve' : 'Answer' },
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
for (const t of tickets) {
|
|
271
|
+
if (t.column !== 'to_review')
|
|
272
|
+
continue;
|
|
273
|
+
out.push({
|
|
274
|
+
id: `review:${t.id}`,
|
|
275
|
+
kind: 'review',
|
|
276
|
+
title: scrubText(t.label ? `${t.label} ${t.title}` : t.title),
|
|
277
|
+
...(teamName(t.teamId, t.assignee) ? { team: teamName(t.teamId, t.assignee) } : {}),
|
|
278
|
+
...(agentName(t.assignee) ? { agent: agentName(t.assignee) } : {}),
|
|
279
|
+
since: t.submittedAt ?? t.updatedAt,
|
|
280
|
+
action: { label: 'Review' },
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
for (const h of holds)
|
|
284
|
+
out.push({ ...h, title: scrubText(h.title) });
|
|
285
|
+
return out.sort((a, b) => Date.parse(a.since) - Date.parse(b.since)).slice(0, C.MAX_WAITING);
|
|
286
|
+
}
|
|
287
|
+
stuck(workItems, tickets, roster, now, teamName, agentName) {
|
|
288
|
+
const out = [];
|
|
289
|
+
const ownerWords = /owner|approv|sign[- ]?in|log ?in|credential|password|permission|reconnect|authori[sz]/i;
|
|
290
|
+
const add = (id, title, target, since, on, reason, teamId) => {
|
|
291
|
+
out.push({
|
|
292
|
+
id,
|
|
293
|
+
title: scrubText(title),
|
|
294
|
+
...(teamName(teamId, target) ? { team: teamName(teamId, target) } : {}),
|
|
295
|
+
...(agentName(target) ? { agent: agentName(target) } : {}),
|
|
296
|
+
since: iso(Number.isFinite(since) ? since : now),
|
|
297
|
+
on,
|
|
298
|
+
reason: scrubText(reason),
|
|
299
|
+
});
|
|
300
|
+
};
|
|
301
|
+
for (const w of workItems) {
|
|
302
|
+
const changed = msOf(w.statusChangedAt) || msOf(w.createdAt);
|
|
303
|
+
if (w.status === 'blocked') {
|
|
304
|
+
const reason = w.blockedReason ?? 'Blocked';
|
|
305
|
+
const on = w.blockSource === 'explicit' ? (ownerWords.test(reason) ? 'owner' : 'agent') : 'system';
|
|
306
|
+
add(`wi:${w.id}`, w.title, w.target, changed, on, reason);
|
|
307
|
+
}
|
|
308
|
+
else if (w.status === 'escalated') {
|
|
309
|
+
add(`wi:${w.id}`, w.title, w.target, changed, 'agent', 'Escalated and not resolved yet');
|
|
310
|
+
}
|
|
311
|
+
else if (w.status === 'failed') {
|
|
312
|
+
const at = msOf(w.completedAt) || changed;
|
|
313
|
+
if (now - at <= C.STUCK_FAILED_MAX_AGE_MS)
|
|
314
|
+
add(`wi:${w.id}`, w.title, w.target, at, 'agent', `Failed${w.error ? `: ${w.error}` : ''}`);
|
|
315
|
+
}
|
|
316
|
+
else if (w.status === 'queued') {
|
|
317
|
+
const waited = now - msOf(w.createdAt);
|
|
318
|
+
const scheduledLater = msOf(w.scheduledAt) > now;
|
|
319
|
+
if (waited >= C.STUCK_QUEUED_MS && !scheduledLater) {
|
|
320
|
+
add(`wi:${w.id}`, w.title, w.target, msOf(w.createdAt), w.target ? 'agent' : 'system', `Waiting in the queue for ${age(waited)}`);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
else if (w.status === 'running') {
|
|
324
|
+
const ran = now - (msOf(w.startedAt) || changed);
|
|
325
|
+
if (ran >= C.STUCK_RUNNING_MS)
|
|
326
|
+
add(`wi:${w.id}`, w.title, w.target, msOf(w.startedAt) || changed, 'agent', `Running for ${age(ran)}`);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
for (const t of tickets) {
|
|
330
|
+
if (t.column === 'blocked')
|
|
331
|
+
add(`ticket:${t.id}`, t.label ? `${t.label} ${t.title}` : t.title, t.assignee, msOf(t.updatedAt), 'agent', 'Ticket is blocked', t.teamId);
|
|
332
|
+
}
|
|
333
|
+
for (const session of roster.keys()) {
|
|
334
|
+
const stalled = this.safeSync(() => this.sources.stalledBackground(session, now), null);
|
|
335
|
+
if (stalled)
|
|
336
|
+
add(`bg:${session}`, stalled.description ?? 'Background task', session, stalled.since, 'agent', `Background task stopped making progress ${age(now - stalled.since)} ago`);
|
|
337
|
+
}
|
|
338
|
+
return out.sort((a, b) => Date.parse(a.since) - Date.parse(b.since)).slice(0, C.MAX_STUCK);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
const round4 = (n) => Math.round(n * 10_000) / 10_000;
|
|
342
|
+
//# sourceMappingURL=board-snapshot.builder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"board-snapshot.builder.js","sourceRoot":"","sources":["../../../../../../backend/src/services/board/board-snapshot.builder.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spend and "what is each agent on" from the usage ledger (crewly#1247).
|
|
3
|
+
* A pure fold over the ledger's in-memory rollup: per-agent and total cost
|
|
4
|
+
* for today (local midnight) and the last 7 days, the owner's minutes, and the
|
|
5
|
+
* open ticket each agent used tokens on most recently (the ledger's
|
|
6
|
+
* attribution, #1250).
|
|
7
|
+
*
|
|
8
|
+
* @module services/board/board-usage
|
|
9
|
+
*/
|
|
10
|
+
import type { UnitRollup } from '../usage-ledger/ledger-rollup.js';
|
|
11
|
+
import type { BoardUsage } from './board-snapshot.builder.js';
|
|
12
|
+
/**
|
|
13
|
+
* Fold the ledger rollup into board usage.
|
|
14
|
+
*
|
|
15
|
+
* @param units - The rollup's units
|
|
16
|
+
* @param now - Epoch ms
|
|
17
|
+
* @returns Usage for the board
|
|
18
|
+
*/
|
|
19
|
+
export declare function computeBoardUsage(units: Iterable<UnitRollup>, now: number): BoardUsage;
|
|
20
|
+
//# sourceMappingURL=board-usage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"board-usage.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/board/board-usage.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kCAAkC,CAAC;AAGnE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAM9D;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,QAAQ,CAAC,UAAU,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,UAAU,CAuDtF"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spend and "what is each agent on" from the usage ledger (crewly#1247).
|
|
3
|
+
* A pure fold over the ledger's in-memory rollup: per-agent and total cost
|
|
4
|
+
* for today (local midnight) and the last 7 days, the owner's minutes, and the
|
|
5
|
+
* open ticket each agent used tokens on most recently (the ledger's
|
|
6
|
+
* attribution, #1250).
|
|
7
|
+
*
|
|
8
|
+
* @module services/board/board-usage
|
|
9
|
+
*/
|
|
10
|
+
import { BOARD_CONSTANTS } from '../../constants.js';
|
|
11
|
+
import { buildOwnerSpans, ownerMinutesByUnit } from '../usage-ledger/owner-spans.js';
|
|
12
|
+
import { scrubText } from './board-redact.js';
|
|
13
|
+
import { startOfLocalDay } from './board-snapshot.builder.js';
|
|
14
|
+
const HOUR_MS = 3_600_000;
|
|
15
|
+
const WEEK_MS = 7 * 24 * HOUR_MS;
|
|
16
|
+
/**
|
|
17
|
+
* Fold the ledger rollup into board usage.
|
|
18
|
+
*
|
|
19
|
+
* @param units - The rollup's units
|
|
20
|
+
* @param now - Epoch ms
|
|
21
|
+
* @returns Usage for the board
|
|
22
|
+
*/
|
|
23
|
+
export function computeBoardUsage(units, now) {
|
|
24
|
+
const dayStart = startOfLocalDay(now);
|
|
25
|
+
const weekStart = now - WEEK_MS;
|
|
26
|
+
const perAgent = new Map();
|
|
27
|
+
const spend = { todayUsd: 0, weekUsd: 0, todayTokens: 0, weekTokens: 0 };
|
|
28
|
+
const recent = new Map();
|
|
29
|
+
const spans = [];
|
|
30
|
+
for (const unit of units) {
|
|
31
|
+
if (unit.ownerMessages.length > 0)
|
|
32
|
+
spans.push(...buildOwnerSpans(unit.unitId, unit.ownerMessages));
|
|
33
|
+
for (const e of unit.usage) {
|
|
34
|
+
const end = e.hourMs + HOUR_MS;
|
|
35
|
+
if (end <= weekStart || e.hourMs > now)
|
|
36
|
+
continue;
|
|
37
|
+
const tokens = e.budget * e.share;
|
|
38
|
+
const usd = e.costUsd * e.share;
|
|
39
|
+
spend.weekUsd += usd;
|
|
40
|
+
spend.weekTokens += tokens;
|
|
41
|
+
if (e.hourMs + HOUR_MS > dayStart) {
|
|
42
|
+
spend.todayUsd += usd;
|
|
43
|
+
spend.todayTokens += tokens;
|
|
44
|
+
const a = perAgent.get(e.agent) ?? { todayUsd: 0, todayTokens: 0 };
|
|
45
|
+
a.todayUsd += usd;
|
|
46
|
+
a.todayTokens += tokens;
|
|
47
|
+
perAgent.set(e.agent, a);
|
|
48
|
+
}
|
|
49
|
+
if (!unit.synthetic && !unit.closedAt) {
|
|
50
|
+
const at = Math.min(now, e.hourMs + HOUR_MS);
|
|
51
|
+
const prev = recent.get(e.agent);
|
|
52
|
+
if (!prev || at > prev.at)
|
|
53
|
+
recent.set(e.agent, { unit, at });
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
const taskOf = new Map();
|
|
58
|
+
for (const [agent, { unit, at }] of recent) {
|
|
59
|
+
if (now - at > BOARD_CONSTANTS.LEDGER_TASK_MAX_AGE_MS)
|
|
60
|
+
continue;
|
|
61
|
+
const title = scrubText(unit.title || unit.ticketId || '');
|
|
62
|
+
if (!title)
|
|
63
|
+
continue;
|
|
64
|
+
taskOf.set(agent, { ...(unit.ticketId ? { id: unit.ticketId } : {}), title, since: new Date(unit.firstMs || at).toISOString() });
|
|
65
|
+
}
|
|
66
|
+
const sum = (m) => [...m.values()].reduce((a, b) => a + b, 0);
|
|
67
|
+
const minutes = (from) => Math.round(sum(ownerMinutesByUnit(spans, from, now)));
|
|
68
|
+
return {
|
|
69
|
+
perAgent,
|
|
70
|
+
taskOf,
|
|
71
|
+
spend: {
|
|
72
|
+
todayUsd: round2(spend.todayUsd),
|
|
73
|
+
weekUsd: round2(spend.weekUsd),
|
|
74
|
+
todayTokens: Math.round(spend.todayTokens),
|
|
75
|
+
weekTokens: Math.round(spend.weekTokens),
|
|
76
|
+
ownerMinutesToday: minutes(dayStart),
|
|
77
|
+
ownerMinutesWeek: minutes(weekStart),
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
const round2 = (n) => Math.round(n * 100) / 100;
|
|
82
|
+
//# sourceMappingURL=board-usage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"board-usage.js","sourceRoot":"","sources":["../../../../../../backend/src/services/board/board-usage.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Owner Board on this machine (crewly#1247): the snapshot builder, the
|
|
3
|
+
* live activity tracker and the Cloud push, behind one handle. The local
|
|
4
|
+
* `GET /api/board` and the push share one builder, so the dashboard and the
|
|
5
|
+
* phone show the same board.
|
|
6
|
+
*
|
|
7
|
+
* @module services/board/board.service
|
|
8
|
+
*/
|
|
9
|
+
import { BoardActivityTracker } from './board-activity.tracker.js';
|
|
10
|
+
import { BoardPushService, type BoardCloudSession } from './board-push.service.js';
|
|
11
|
+
import { BoardSnapshotBuilder, type BoardSources } from './board-snapshot.builder.js';
|
|
12
|
+
import type { BoardSnapshot, BoardTimelineEvent } from './board.types.js';
|
|
13
|
+
/** Constructor options. */
|
|
14
|
+
export interface BoardServiceOptions {
|
|
15
|
+
sources: BoardSources;
|
|
16
|
+
cloud: BoardCloudSession;
|
|
17
|
+
tracker?: BoardActivityTracker;
|
|
18
|
+
env?: NodeJS.ProcessEnv;
|
|
19
|
+
}
|
|
20
|
+
/** Owner board for this machine. */
|
|
21
|
+
export declare class BoardService {
|
|
22
|
+
readonly builder: BoardSnapshotBuilder;
|
|
23
|
+
readonly push: BoardPushService;
|
|
24
|
+
readonly tracker: BoardActivityTracker;
|
|
25
|
+
private last;
|
|
26
|
+
private inflight;
|
|
27
|
+
constructor(options: BoardServiceOptions);
|
|
28
|
+
/** Start the Cloud push (a no-op when switched off). */
|
|
29
|
+
start(): void;
|
|
30
|
+
/** Stop the Cloud push. */
|
|
31
|
+
stop(): void;
|
|
32
|
+
/**
|
|
33
|
+
* The current snapshot; one built within `maxAgeMs` is reused, and
|
|
34
|
+
* concurrent callers share one build.
|
|
35
|
+
*
|
|
36
|
+
* @param maxAgeMs - Oldest acceptable snapshot (ms)
|
|
37
|
+
* @returns The snapshot
|
|
38
|
+
*/
|
|
39
|
+
getSnapshot(maxAgeMs?: number): Promise<BoardSnapshot>;
|
|
40
|
+
/**
|
|
41
|
+
* An agent's recent timeline.
|
|
42
|
+
*
|
|
43
|
+
* @param session - Agent session
|
|
44
|
+
* @returns Events, oldest first
|
|
45
|
+
*/
|
|
46
|
+
timeline(session: string): BoardTimelineEvent[];
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Install the process-wide board (composition root).
|
|
50
|
+
*
|
|
51
|
+
* @param service - The board, or null
|
|
52
|
+
*/
|
|
53
|
+
export declare function setBoardService(service: BoardService | null): void;
|
|
54
|
+
/**
|
|
55
|
+
* The process-wide board, or null before boot wired it.
|
|
56
|
+
*
|
|
57
|
+
* @returns The board or null
|
|
58
|
+
*/
|
|
59
|
+
export declare function getBoardService(): BoardService | null;
|
|
60
|
+
//# sourceMappingURL=board.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"board.service.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/board/board.service.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AACnE,OAAO,EAAE,gBAAgB,EAAE,KAAK,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AACnF,OAAO,EAAE,oBAAoB,EAAE,KAAK,YAAY,EAAE,MAAM,6BAA6B,CAAC;AACtF,OAAO,KAAK,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAE1E,2BAA2B;AAC3B,MAAM,WAAW,mBAAmB;IACnC,OAAO,EAAE,YAAY,CAAC;IACtB,KAAK,EAAE,iBAAiB,CAAC;IACzB,OAAO,CAAC,EAAE,oBAAoB,CAAC;IAC/B,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;CACxB;AAED,oCAAoC;AACpC,qBAAa,YAAY;IACxB,QAAQ,CAAC,OAAO,EAAE,oBAAoB,CAAC;IACvC,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAC;IAChC,QAAQ,CAAC,OAAO,EAAE,oBAAoB,CAAC;IACvC,OAAO,CAAC,IAAI,CAAwD;IACpE,OAAO,CAAC,QAAQ,CAAuC;gBAE3C,OAAO,EAAE,mBAAmB;IAMxC,wDAAwD;IACxD,KAAK,IAAI,IAAI;IAIb,2BAA2B;IAC3B,IAAI,IAAI,IAAI;IAIZ;;;;;;OAMG;IACG,WAAW,CAAC,QAAQ,SAAQ,GAAG,OAAO,CAAC,aAAa,CAAC;IAgB3D;;;;;OAKG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,kBAAkB,EAAE;CAG/C;AAID;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI,GAAG,IAAI,CAElE;AAED;;;;GAIG;AACH,wBAAgB,eAAe,IAAI,YAAY,GAAG,IAAI,CAErD"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Owner Board on this machine (crewly#1247): the snapshot builder, the
|
|
3
|
+
* live activity tracker and the Cloud push, behind one handle. The local
|
|
4
|
+
* `GET /api/board` and the push share one builder, so the dashboard and the
|
|
5
|
+
* phone show the same board.
|
|
6
|
+
*
|
|
7
|
+
* @module services/board/board.service
|
|
8
|
+
*/
|
|
9
|
+
import { BoardActivityTracker } from './board-activity.tracker.js';
|
|
10
|
+
import { BoardPushService } from './board-push.service.js';
|
|
11
|
+
import { BoardSnapshotBuilder } from './board-snapshot.builder.js';
|
|
12
|
+
/** Owner board for this machine. */
|
|
13
|
+
export class BoardService {
|
|
14
|
+
builder;
|
|
15
|
+
push;
|
|
16
|
+
tracker;
|
|
17
|
+
last = null;
|
|
18
|
+
inflight = null;
|
|
19
|
+
constructor(options) {
|
|
20
|
+
this.tracker = options.tracker ?? BoardActivityTracker.getInstance();
|
|
21
|
+
this.builder = new BoardSnapshotBuilder(options.sources, this.tracker);
|
|
22
|
+
this.push = new BoardPushService({ builder: this.builder, tracker: this.tracker, cloud: options.cloud, ...(options.env ? { env: options.env } : {}) });
|
|
23
|
+
}
|
|
24
|
+
/** Start the Cloud push (a no-op when switched off). */
|
|
25
|
+
start() {
|
|
26
|
+
this.push.start();
|
|
27
|
+
}
|
|
28
|
+
/** Stop the Cloud push. */
|
|
29
|
+
stop() {
|
|
30
|
+
this.push.stop();
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* The current snapshot; one built within `maxAgeMs` is reused, and
|
|
34
|
+
* concurrent callers share one build.
|
|
35
|
+
*
|
|
36
|
+
* @param maxAgeMs - Oldest acceptable snapshot (ms)
|
|
37
|
+
* @returns The snapshot
|
|
38
|
+
*/
|
|
39
|
+
async getSnapshot(maxAgeMs = 5_000) {
|
|
40
|
+
if (this.last && Date.now() - this.last.at < maxAgeMs)
|
|
41
|
+
return this.last.snapshot;
|
|
42
|
+
if (!this.inflight) {
|
|
43
|
+
this.inflight = this.builder
|
|
44
|
+
.build()
|
|
45
|
+
.then((snapshot) => {
|
|
46
|
+
this.last = { at: Date.now(), snapshot };
|
|
47
|
+
return snapshot;
|
|
48
|
+
})
|
|
49
|
+
.finally(() => {
|
|
50
|
+
this.inflight = null;
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
return this.inflight;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* An agent's recent timeline.
|
|
57
|
+
*
|
|
58
|
+
* @param session - Agent session
|
|
59
|
+
* @returns Events, oldest first
|
|
60
|
+
*/
|
|
61
|
+
timeline(session) {
|
|
62
|
+
return this.tracker.timeline(session);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
let instance = null;
|
|
66
|
+
/**
|
|
67
|
+
* Install the process-wide board (composition root).
|
|
68
|
+
*
|
|
69
|
+
* @param service - The board, or null
|
|
70
|
+
*/
|
|
71
|
+
export function setBoardService(service) {
|
|
72
|
+
instance = service;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* The process-wide board, or null before boot wired it.
|
|
76
|
+
*
|
|
77
|
+
* @returns The board or null
|
|
78
|
+
*/
|
|
79
|
+
export function getBoardService() {
|
|
80
|
+
return instance;
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=board.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"board.service.js","sourceRoot":"","sources":["../../../../../../backend/src/services/board/board.service.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AACnE,OAAO,EAAE,gBAAgB,EAA0B,MAAM,yBAAyB,CAAC;AACnF,OAAO,EAAE,oBAAoB,EAAqB,MAAM,6BAA6B,CAAC;AAWtF,oCAAoC;AACpC,MAAM,OAAO,YAAY;IACf,OAAO,CAAuB;IAC9B,IAAI,CAAmB;IACvB,OAAO,CAAuB;IAC/B,IAAI,GAAmD,IAAI,CAAC;IAC5D,QAAQ,GAAkC,IAAI,CAAC;IAEvD,YAAY,OAA4B;QACvC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,oBAAoB,CAAC,WAAW,EAAE,CAAC;QACrE,IAAI,CAAC,OAAO,GAAG,IAAI,oBAAoB,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QACvE,IAAI,CAAC,IAAI,GAAG,IAAI,gBAAgB,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;IACxJ,CAAC;IAED,wDAAwD;IACxD,KAAK;QACJ,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;IACnB,CAAC;IAED,2BAA2B;IAC3B,IAAI;QACH,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;IAClB,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,WAAW,CAAC,QAAQ,GAAG,KAAK;QACjC,IAAI,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,GAAG,QAAQ;YAAE,OAAO,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC;QACjF,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;YACpB,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,OAAO;iBAC1B,KAAK,EAAE;iBACP,IAAI,CAAC,CAAC,QAAQ,EAAE,EAAE;gBAClB,IAAI,CAAC,IAAI,GAAG,EAAE,EAAE,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,QAAQ,EAAE,CAAC;gBACzC,OAAO,QAAQ,CAAC;YACjB,CAAC,CAAC;iBACD,OAAO,CAAC,GAAG,EAAE;gBACb,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;YACtB,CAAC,CAAC,CAAC;QACL,CAAC;QACD,OAAO,IAAI,CAAC,QAAQ,CAAC;IACtB,CAAC;IAED;;;;;OAKG;IACH,QAAQ,CAAC,OAAe;QACvB,OAAO,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IACvC,CAAC;CACD;AAED,IAAI,QAAQ,GAAwB,IAAI,CAAC;AAEzC;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,OAA4B;IAC3D,QAAQ,GAAG,OAAO,CAAC;AACpB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,eAAe;IAC9B,OAAO,QAAQ,CAAC;AACjB,CAAC"}
|