crewly 1.20.302 → 1.20.303
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/roles/developer/prompt.md +7 -0
- package/config/roles/fullstack-dev/prompt.md +7 -0
- package/config/roles/product-manager/prompt.md +7 -0
- package/config/skills/orchestrator/usage-categories/SKILL.md +59 -0
- package/config/skills/orchestrator/usage-categories/execute.sh +81 -0
- package/config/skills/orchestrator/usage-categories/execute.test.sh +45 -0
- package/config/skills/orchestrator/usage-report/SKILL.md +71 -0
- package/config/skills/orchestrator/usage-report/execute.sh +84 -0
- package/config/skills/orchestrator/usage-report/execute.test.sh +39 -0
- package/config/skills/team-leader/usage-report/SKILL.md +71 -0
- package/config/skills/team-leader/usage-report/execute.sh +84 -0
- package/config/skills/team-leader/usage-report/execute.test.sh +39 -0
- package/dist/backend/backend/src/constants.d.ts +11 -0
- package/dist/backend/backend/src/constants.d.ts.map +1 -1
- package/dist/backend/backend/src/constants.js +11 -0
- package/dist/backend/backend/src/constants.js.map +1 -1
- package/dist/backend/backend/src/controllers/usage-ledger/usage-ledger.controller.d.ts +36 -0
- package/dist/backend/backend/src/controllers/usage-ledger/usage-ledger.controller.d.ts.map +1 -0
- package/dist/backend/backend/src/controllers/usage-ledger/usage-ledger.controller.js +158 -0
- package/dist/backend/backend/src/controllers/usage-ledger/usage-ledger.controller.js.map +1 -0
- package/dist/backend/backend/src/controllers/usage-ledger/usage-ledger.routes.d.ts +15 -0
- package/dist/backend/backend/src/controllers/usage-ledger/usage-ledger.routes.d.ts.map +1 -0
- package/dist/backend/backend/src/controllers/usage-ledger/usage-ledger.routes.js +26 -0
- package/dist/backend/backend/src/controllers/usage-ledger/usage-ledger.routes.js.map +1 -0
- package/dist/backend/backend/src/index.d.ts +10 -0
- package/dist/backend/backend/src/index.d.ts.map +1 -1
- package/dist/backend/backend/src/index.js +75 -17
- 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/agent/follow-through.wiring.d.ts +32 -0
- package/dist/backend/backend/src/services/agent/follow-through.wiring.d.ts.map +1 -1
- package/dist/backend/backend/src/services/agent/follow-through.wiring.js +42 -1
- package/dist/backend/backend/src/services/agent/follow-through.wiring.js.map +1 -1
- package/dist/backend/backend/src/services/agent/resource-mode.service.d.ts +26 -6
- package/dist/backend/backend/src/services/agent/resource-mode.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/agent/resource-mode.service.js +59 -5
- package/dist/backend/backend/src/services/agent/resource-mode.service.js.map +1 -1
- package/dist/backend/backend/src/services/monitoring/agent-turn-state.d.ts +35 -0
- package/dist/backend/backend/src/services/monitoring/agent-turn-state.d.ts.map +1 -1
- package/dist/backend/backend/src/services/monitoring/agent-turn-state.js +101 -0
- package/dist/backend/backend/src/services/monitoring/agent-turn-state.js.map +1 -1
- package/dist/backend/backend/src/services/monitoring/background-work-watchdog.d.ts +58 -0
- package/dist/backend/backend/src/services/monitoring/background-work-watchdog.d.ts.map +1 -0
- package/dist/backend/backend/src/services/monitoring/background-work-watchdog.js +100 -0
- package/dist/backend/backend/src/services/monitoring/background-work-watchdog.js.map +1 -0
- package/dist/backend/backend/src/services/monitoring/token-usage.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/monitoring/token-usage.service.js +3 -0
- package/dist/backend/backend/src/services/monitoring/token-usage.service.js.map +1 -1
- package/dist/backend/backend/src/services/reconciler/reconciler-data-provider.d.ts +7 -0
- package/dist/backend/backend/src/services/reconciler/reconciler-data-provider.d.ts.map +1 -1
- package/dist/backend/backend/src/services/reconciler/reconciler-data-provider.js +15 -0
- package/dist/backend/backend/src/services/reconciler/reconciler-data-provider.js.map +1 -1
- package/dist/backend/backend/src/services/slack/slack-typing-placeholder.service.d.ts +24 -0
- package/dist/backend/backend/src/services/slack/slack-typing-placeholder.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/slack/slack-typing-placeholder.service.js +63 -1
- package/dist/backend/backend/src/services/slack/slack-typing-placeholder.service.js.map +1 -1
- package/dist/backend/backend/src/services/trace/trace-context.service.d.ts +42 -2
- package/dist/backend/backend/src/services/trace/trace-context.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/trace/trace-context.service.js +66 -5
- package/dist/backend/backend/src/services/trace/trace-context.service.js.map +1 -1
- package/dist/backend/backend/src/services/trace/trace-recorder.d.ts.map +1 -1
- package/dist/backend/backend/src/services/trace/trace-recorder.js +11 -0
- package/dist/backend/backend/src/services/trace/trace-recorder.js.map +1 -1
- package/dist/backend/backend/src/services/usage-ledger/categories-param.d.ts +28 -0
- package/dist/backend/backend/src/services/usage-ledger/categories-param.d.ts.map +1 -0
- package/dist/backend/backend/src/services/usage-ledger/categories-param.js +67 -0
- package/dist/backend/backend/src/services/usage-ledger/categories-param.js.map +1 -0
- package/dist/backend/backend/src/services/usage-ledger/excerpt.d.ts +37 -0
- package/dist/backend/backend/src/services/usage-ledger/excerpt.d.ts.map +1 -0
- package/dist/backend/backend/src/services/usage-ledger/excerpt.js +49 -0
- package/dist/backend/backend/src/services/usage-ledger/excerpt.js.map +1 -0
- package/dist/backend/backend/src/services/usage-ledger/index.d.ts +14 -0
- package/dist/backend/backend/src/services/usage-ledger/index.d.ts.map +1 -0
- package/dist/backend/backend/src/services/usage-ledger/index.js +14 -0
- package/dist/backend/backend/src/services/usage-ledger/index.js.map +1 -0
- package/dist/backend/backend/src/services/usage-ledger/ledger-hooks.d.ts +44 -0
- package/dist/backend/backend/src/services/usage-ledger/ledger-hooks.d.ts.map +1 -0
- package/dist/backend/backend/src/services/usage-ledger/ledger-hooks.js +52 -0
- package/dist/backend/backend/src/services/usage-ledger/ledger-hooks.js.map +1 -0
- package/dist/backend/backend/src/services/usage-ledger/ledger-rollup.d.ts +100 -0
- package/dist/backend/backend/src/services/usage-ledger/ledger-rollup.d.ts.map +1 -0
- package/dist/backend/backend/src/services/usage-ledger/ledger-rollup.js +189 -0
- package/dist/backend/backend/src/services/usage-ledger/ledger-rollup.js.map +1 -0
- package/dist/backend/backend/src/services/usage-ledger/owner-spans.d.ts +41 -0
- package/dist/backend/backend/src/services/usage-ledger/owner-spans.d.ts.map +1 -0
- package/dist/backend/backend/src/services/usage-ledger/owner-spans.js +71 -0
- package/dist/backend/backend/src/services/usage-ledger/owner-spans.js.map +1 -0
- package/dist/backend/backend/src/services/usage-ledger/team-directory.d.ts +47 -0
- package/dist/backend/backend/src/services/usage-ledger/team-directory.d.ts.map +1 -0
- package/dist/backend/backend/src/services/usage-ledger/team-directory.js +106 -0
- package/dist/backend/backend/src/services/usage-ledger/team-directory.js.map +1 -0
- package/dist/backend/backend/src/services/usage-ledger/unit-classifier.d.ts +101 -0
- package/dist/backend/backend/src/services/usage-ledger/unit-classifier.d.ts.map +1 -0
- package/dist/backend/backend/src/services/usage-ledger/unit-classifier.js +263 -0
- package/dist/backend/backend/src/services/usage-ledger/unit-classifier.js.map +1 -0
- package/dist/backend/backend/src/services/usage-ledger/usage-categories.store.d.ts +96 -0
- package/dist/backend/backend/src/services/usage-ledger/usage-categories.store.d.ts.map +1 -0
- package/dist/backend/backend/src/services/usage-ledger/usage-categories.store.js +166 -0
- package/dist/backend/backend/src/services/usage-ledger/usage-categories.store.js.map +1 -0
- package/dist/backend/backend/src/services/usage-ledger/usage-ledger.backfill.d.ts +90 -0
- package/dist/backend/backend/src/services/usage-ledger/usage-ledger.backfill.d.ts.map +1 -0
- package/dist/backend/backend/src/services/usage-ledger/usage-ledger.backfill.js +294 -0
- package/dist/backend/backend/src/services/usage-ledger/usage-ledger.backfill.js.map +1 -0
- package/dist/backend/backend/src/services/usage-ledger/usage-ledger.recorder.d.ts +102 -0
- package/dist/backend/backend/src/services/usage-ledger/usage-ledger.recorder.d.ts.map +1 -0
- package/dist/backend/backend/src/services/usage-ledger/usage-ledger.recorder.js +314 -0
- package/dist/backend/backend/src/services/usage-ledger/usage-ledger.recorder.js.map +1 -0
- package/dist/backend/backend/src/services/usage-ledger/usage-ledger.runtime.d.ts +62 -0
- package/dist/backend/backend/src/services/usage-ledger/usage-ledger.runtime.d.ts.map +1 -0
- package/dist/backend/backend/src/services/usage-ledger/usage-ledger.runtime.js +106 -0
- package/dist/backend/backend/src/services/usage-ledger/usage-ledger.runtime.js.map +1 -0
- package/dist/backend/backend/src/services/usage-ledger/usage-ledger.store.d.ts +52 -0
- package/dist/backend/backend/src/services/usage-ledger/usage-ledger.store.d.ts.map +1 -0
- package/dist/backend/backend/src/services/usage-ledger/usage-ledger.store.js +95 -0
- package/dist/backend/backend/src/services/usage-ledger/usage-ledger.store.js.map +1 -0
- package/dist/backend/backend/src/services/usage-ledger/usage-ledger.types.d.ts +127 -0
- package/dist/backend/backend/src/services/usage-ledger/usage-ledger.types.d.ts.map +1 -0
- package/dist/backend/backend/src/services/usage-ledger/usage-ledger.types.js +19 -0
- package/dist/backend/backend/src/services/usage-ledger/usage-ledger.types.js.map +1 -0
- package/dist/backend/backend/src/services/usage-ledger/usage-report.service.d.ts +124 -0
- package/dist/backend/backend/src/services/usage-ledger/usage-report.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/usage-ledger/usage-report.service.js +372 -0
- package/dist/backend/backend/src/services/usage-ledger/usage-report.service.js.map +1 -0
- package/dist/backend/build-info.json +2 -2
- package/dist/cli/backend/src/constants.d.ts +11 -0
- package/dist/cli/backend/src/constants.d.ts.map +1 -1
- package/dist/cli/backend/src/constants.js +11 -0
- package/dist/cli/backend/src/constants.js.map +1 -1
- package/dist/cli/backend/src/services/agent/resource-mode.service.d.ts +26 -6
- package/dist/cli/backend/src/services/agent/resource-mode.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/agent/resource-mode.service.js +59 -5
- package/dist/cli/backend/src/services/agent/resource-mode.service.js.map +1 -1
- package/dist/cli/backend/src/services/monitoring/agent-turn-state.d.ts +35 -0
- package/dist/cli/backend/src/services/monitoring/agent-turn-state.d.ts.map +1 -1
- package/dist/cli/backend/src/services/monitoring/agent-turn-state.js +101 -0
- package/dist/cli/backend/src/services/monitoring/agent-turn-state.js.map +1 -1
- package/dist/cli/backend/src/services/monitoring/token-usage.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/monitoring/token-usage.service.js +3 -0
- package/dist/cli/backend/src/services/monitoring/token-usage.service.js.map +1 -1
- package/dist/cli/backend/src/services/slack/slack-typing-placeholder.service.d.ts +24 -0
- package/dist/cli/backend/src/services/slack/slack-typing-placeholder.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/slack/slack-typing-placeholder.service.js +63 -1
- package/dist/cli/backend/src/services/slack/slack-typing-placeholder.service.js.map +1 -1
- package/dist/cli/backend/src/services/trace/trace-context.service.d.ts +42 -2
- package/dist/cli/backend/src/services/trace/trace-context.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/trace/trace-context.service.js +66 -5
- package/dist/cli/backend/src/services/trace/trace-context.service.js.map +1 -1
- package/dist/cli/backend/src/services/trace/trace-recorder.d.ts.map +1 -1
- package/dist/cli/backend/src/services/trace/trace-recorder.js +11 -0
- package/dist/cli/backend/src/services/trace/trace-recorder.js.map +1 -1
- package/dist/cli/backend/src/services/usage-ledger/ledger-hooks.d.ts +44 -0
- package/dist/cli/backend/src/services/usage-ledger/ledger-hooks.d.ts.map +1 -0
- package/dist/cli/backend/src/services/usage-ledger/ledger-hooks.js +52 -0
- package/dist/cli/backend/src/services/usage-ledger/ledger-hooks.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The owner's own categories for usage reports ("self-media", "book", …).
|
|
3
|
+
*
|
|
4
|
+
* Categories are optional and never written into ticket facts: they only
|
|
5
|
+
* decide how a report groups tickets. Each definition has a natural-language
|
|
6
|
+
* description and optional hints (keywords, team names, project names). The
|
|
7
|
+
* set carries a version that changes whenever a definition changes; the
|
|
8
|
+
* classification cache is keyed on it.
|
|
9
|
+
*
|
|
10
|
+
* @module services/usage-ledger/usage-categories.store
|
|
11
|
+
*/
|
|
12
|
+
import { createHash } from 'crypto';
|
|
13
|
+
import { promises as fs } from 'fs';
|
|
14
|
+
import * as path from 'path';
|
|
15
|
+
import { atomicWriteJson } from '../../utils/file-io.utils.js';
|
|
16
|
+
import { getCrewlyHomePath } from '../core/crewly-home.utils.js';
|
|
17
|
+
import { LEDGER_DIR_NAME } from './usage-ledger.store.js';
|
|
18
|
+
/** File holding the definitions. */
|
|
19
|
+
export const CATEGORIES_FILE = 'categories.json';
|
|
20
|
+
/** Longest name / description / hint accepted. */
|
|
21
|
+
const MAX_TEXT = 400;
|
|
22
|
+
const MAX_HINTS = 60;
|
|
23
|
+
const MAX_CATEGORIES = 40;
|
|
24
|
+
/**
|
|
25
|
+
* Clean a list of hints.
|
|
26
|
+
*
|
|
27
|
+
* @param value - Candidate list
|
|
28
|
+
* @returns Trimmed, de-duplicated strings (empty list becomes undefined)
|
|
29
|
+
*/
|
|
30
|
+
function cleanList(value) {
|
|
31
|
+
if (!Array.isArray(value))
|
|
32
|
+
return undefined;
|
|
33
|
+
const out = [...new Set(value.filter((v) => typeof v === 'string').map((v) => v.trim().slice(0, 120)).filter(Boolean))];
|
|
34
|
+
return out.length > 0 ? out.slice(0, MAX_HINTS) : undefined;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Validate and normalise a definition.
|
|
38
|
+
*
|
|
39
|
+
* @param input - Candidate
|
|
40
|
+
* @returns The definition, or an error text
|
|
41
|
+
*/
|
|
42
|
+
export function normalizeCategory(input) {
|
|
43
|
+
if (!input || typeof input !== 'object')
|
|
44
|
+
return 'category must be an object';
|
|
45
|
+
const raw = input;
|
|
46
|
+
const name = typeof raw.name === 'string' ? raw.name.trim().slice(0, 60) : '';
|
|
47
|
+
if (!name)
|
|
48
|
+
return 'category needs a name';
|
|
49
|
+
if (name.toLowerCase() === 'other')
|
|
50
|
+
return '"other" is reserved for tickets that match nothing';
|
|
51
|
+
const description = typeof raw.description === 'string' && raw.description.trim() ? raw.description.trim().slice(0, MAX_TEXT) : undefined;
|
|
52
|
+
const keywords = cleanList(raw.keywords);
|
|
53
|
+
const teams = cleanList(raw.teams);
|
|
54
|
+
const projects = cleanList(raw.projects);
|
|
55
|
+
return {
|
|
56
|
+
name,
|
|
57
|
+
...(description ? { description } : {}),
|
|
58
|
+
...(keywords ? { keywords } : {}),
|
|
59
|
+
...(teams ? { teams } : {}),
|
|
60
|
+
...(projects ? { projects } : {}),
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Identity of a set of definitions: the same definitions always give the same
|
|
65
|
+
* id, so a classification cache survives a set being re-entered unchanged.
|
|
66
|
+
*
|
|
67
|
+
* @param defs - Definitions
|
|
68
|
+
* @returns `default` for none, else a short hash
|
|
69
|
+
*/
|
|
70
|
+
export function categorySetId(defs) {
|
|
71
|
+
if (defs.length === 0)
|
|
72
|
+
return 'default';
|
|
73
|
+
const canonical = defs.map((d) => [d.name.toLowerCase(), d.description ?? '', d.keywords ?? [], d.teams ?? [], d.projects ?? []]);
|
|
74
|
+
return createHash('sha1').update(JSON.stringify(canonical)).digest('hex').slice(0, 10);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* File-backed category definitions.
|
|
78
|
+
*/
|
|
79
|
+
export class UsageCategoriesStore {
|
|
80
|
+
dir;
|
|
81
|
+
file;
|
|
82
|
+
/**
|
|
83
|
+
* @param dir - Ledger directory (default `~/.crewly/usage-ledger`)
|
|
84
|
+
*/
|
|
85
|
+
constructor(dir = path.join(getCrewlyHomePath(), LEDGER_DIR_NAME)) {
|
|
86
|
+
this.dir = dir;
|
|
87
|
+
this.file = path.join(dir, CATEGORIES_FILE);
|
|
88
|
+
}
|
|
89
|
+
async read() {
|
|
90
|
+
try {
|
|
91
|
+
const parsed = JSON.parse(await fs.readFile(this.file, 'utf-8'));
|
|
92
|
+
const categories = (Array.isArray(parsed.categories) ? parsed.categories : [])
|
|
93
|
+
.map(normalizeCategory)
|
|
94
|
+
.filter((c) => typeof c !== 'string');
|
|
95
|
+
return { version: Number(parsed.version) || 0, updatedAt: parsed.updatedAt ?? '', categories };
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
return { version: 0, updatedAt: '', categories: [] };
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
async write(categories, previousVersion) {
|
|
102
|
+
const next = { version: previousVersion + 1, updatedAt: new Date().toISOString(), categories };
|
|
103
|
+
await fs.mkdir(this.dir, { recursive: true });
|
|
104
|
+
await atomicWriteJson(this.file, next);
|
|
105
|
+
return next;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* The current definitions.
|
|
109
|
+
*
|
|
110
|
+
* @returns Version, set id and definitions
|
|
111
|
+
*/
|
|
112
|
+
async list() {
|
|
113
|
+
const file = await this.read();
|
|
114
|
+
return { version: file.version, setId: categorySetId(file.categories), updatedAt: file.updatedAt, categories: file.categories };
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Add or replace one category (matched by name, case-insensitive).
|
|
118
|
+
*
|
|
119
|
+
* @param input - The definition
|
|
120
|
+
* @returns The new state, or an error text
|
|
121
|
+
*/
|
|
122
|
+
async set(input) {
|
|
123
|
+
const def = normalizeCategory(input);
|
|
124
|
+
if (typeof def === 'string')
|
|
125
|
+
return def;
|
|
126
|
+
const file = await this.read();
|
|
127
|
+
const rest = file.categories.filter((c) => c.name.toLowerCase() !== def.name.toLowerCase());
|
|
128
|
+
if (rest.length >= MAX_CATEGORIES)
|
|
129
|
+
return `at most ${MAX_CATEGORIES} categories`;
|
|
130
|
+
const written = await this.write([...rest, def], file.version);
|
|
131
|
+
return { version: written.version, categories: written.categories };
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Replace every definition.
|
|
135
|
+
*
|
|
136
|
+
* @param inputs - The new set (empty clears it)
|
|
137
|
+
* @returns The new state, or an error text
|
|
138
|
+
*/
|
|
139
|
+
async replaceAll(inputs) {
|
|
140
|
+
const defs = [];
|
|
141
|
+
for (const input of inputs.slice(0, MAX_CATEGORIES)) {
|
|
142
|
+
const def = normalizeCategory(input);
|
|
143
|
+
if (typeof def === 'string')
|
|
144
|
+
return def;
|
|
145
|
+
defs.push(def);
|
|
146
|
+
}
|
|
147
|
+
const file = await this.read();
|
|
148
|
+
const written = await this.write(defs, file.version);
|
|
149
|
+
return { version: written.version, categories: written.categories };
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Remove one category.
|
|
153
|
+
*
|
|
154
|
+
* @param name - Its name (case-insensitive)
|
|
155
|
+
* @returns The new state, or null when there was no such category
|
|
156
|
+
*/
|
|
157
|
+
async remove(name) {
|
|
158
|
+
const file = await this.read();
|
|
159
|
+
const rest = file.categories.filter((c) => c.name.toLowerCase() !== name.trim().toLowerCase());
|
|
160
|
+
if (rest.length === file.categories.length)
|
|
161
|
+
return null;
|
|
162
|
+
const written = await this.write(rest, file.version);
|
|
163
|
+
return { version: written.version, categories: written.categories };
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
//# sourceMappingURL=usage-categories.store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usage-categories.store.js","sourceRoot":"","sources":["../../../../../../backend/src/services/usage-ledger/usage-categories.store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One-time rebuild of the ledger for the time before it existed.
|
|
3
|
+
*
|
|
4
|
+
* Sources: the token ledger (usage events) and the run traces (ticket roots,
|
|
5
|
+
* deliveries, busy turns, status changes). The traces expire after 90 days,
|
|
6
|
+
* the ledger does not, so this keeps what they know.
|
|
7
|
+
*
|
|
8
|
+
* Attribution of a usage event:
|
|
9
|
+
* - it already carries a trace id (the harness stamped it): `recorded`;
|
|
10
|
+
* - otherwise it goes to the trace the session's latest delivery named,
|
|
11
|
+
* when that delivery was at most {@link INFER_MAX_GAP_MS} before the event:
|
|
12
|
+
* `inferred`;
|
|
13
|
+
* - otherwise it lands in the agent's "(no ticket)" unit.
|
|
14
|
+
*
|
|
15
|
+
* Only facts older than the cutoff (the moment live recording began) are
|
|
16
|
+
* written, so history and live facts never overlap. The result goes to
|
|
17
|
+
* `backfill.jsonl`; the run is recorded in `state.json` and never repeats.
|
|
18
|
+
*
|
|
19
|
+
* @module services/usage-ledger/usage-ledger.backfill
|
|
20
|
+
*/
|
|
21
|
+
import { type TokenUsageEvent } from '../monitoring/token-usage.service.js';
|
|
22
|
+
import type { TeamLookup } from './team-directory.js';
|
|
23
|
+
import type { UsageLedgerStore } from './usage-ledger.store.js';
|
|
24
|
+
/** An unticketed event this soon after a delivery is credited to the delivery's trace. */
|
|
25
|
+
export declare const INFER_MAX_GAP_MS: number;
|
|
26
|
+
/** File with the ledger's own bookkeeping. */
|
|
27
|
+
export declare const STATE_FILE = "state.json";
|
|
28
|
+
/** Bookkeeping persisted beside the facts. */
|
|
29
|
+
export interface LedgerState {
|
|
30
|
+
/** When live recording began (the backfill cutoff) */
|
|
31
|
+
liveSince?: string;
|
|
32
|
+
backfill?: {
|
|
33
|
+
doneAt: string;
|
|
34
|
+
cutoff: string;
|
|
35
|
+
facts: number;
|
|
36
|
+
usageEvents: number;
|
|
37
|
+
inferredEvents: number;
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/** Where history comes from. */
|
|
41
|
+
export interface BackfillSources {
|
|
42
|
+
/** Usage events of every session */
|
|
43
|
+
forEachUsage: (visit: (session: string, event: TokenUsageEvent) => void) => void;
|
|
44
|
+
/** The traces directory (index.json + `<id>.jsonl`) */
|
|
45
|
+
traceDir: string;
|
|
46
|
+
directory: TeamLookup;
|
|
47
|
+
}
|
|
48
|
+
/** What a backfill did. */
|
|
49
|
+
export interface BackfillResult {
|
|
50
|
+
ran: boolean;
|
|
51
|
+
reason?: string;
|
|
52
|
+
facts: number;
|
|
53
|
+
usageEvents: number;
|
|
54
|
+
inferredEvents: number;
|
|
55
|
+
traces: number;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Read the ledger bookkeeping.
|
|
59
|
+
*
|
|
60
|
+
* @param dir - Ledger directory
|
|
61
|
+
* @returns State (empty when none)
|
|
62
|
+
*/
|
|
63
|
+
export declare function readLedgerState(dir: string): Promise<LedgerState>;
|
|
64
|
+
/**
|
|
65
|
+
* Write the ledger bookkeeping.
|
|
66
|
+
*
|
|
67
|
+
* @param dir - Ledger directory
|
|
68
|
+
* @param state - State
|
|
69
|
+
*/
|
|
70
|
+
export declare function writeLedgerState(dir: string, state: LedgerState): Promise<void>;
|
|
71
|
+
/**
|
|
72
|
+
* Note when live recording began (once).
|
|
73
|
+
*
|
|
74
|
+
* @param dir - Ledger directory
|
|
75
|
+
* @param now - Now (ms)
|
|
76
|
+
* @returns The cutoff
|
|
77
|
+
*/
|
|
78
|
+
export declare function ensureLiveSince(dir: string, now?: number): Promise<string>;
|
|
79
|
+
/**
|
|
80
|
+
* Rebuild history into `backfill.jsonl`.
|
|
81
|
+
*
|
|
82
|
+
* @param store - Ledger store
|
|
83
|
+
* @param sources - Where history comes from
|
|
84
|
+
* @param opts.cutoffMs - Only facts before this time are written
|
|
85
|
+
* @returns What happened
|
|
86
|
+
*/
|
|
87
|
+
export declare function runBackfill(store: UsageLedgerStore, sources: BackfillSources, opts: {
|
|
88
|
+
cutoffMs: number;
|
|
89
|
+
}): Promise<BackfillResult>;
|
|
90
|
+
//# sourceMappingURL=usage-ledger.backfill.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usage-ledger.backfill.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/usage-ledger/usage-ledger.backfill.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAOH,OAAO,EAA6B,KAAK,eAAe,EAAE,MAAM,sCAAsC,CAAC;AAGvG,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAGhE,0FAA0F;AAC1F,eAAO,MAAM,gBAAgB,QAAgC,CAAC;AAE9D,8CAA8C;AAC9C,eAAO,MAAM,UAAU,eAAe,CAAC;AAEvC,8CAA8C;AAC9C,MAAM,WAAW,WAAW;IAC3B,sDAAsD;IACtD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,MAAM,CAAA;KAAE,CAAC;CAC1G;AAED,gCAAgC;AAChC,MAAM,WAAW,eAAe;IAC/B,oCAAoC;IACpC,YAAY,EAAE,CAAC,KAAK,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,eAAe,KAAK,IAAI,KAAK,IAAI,CAAC;IACjF,uDAAuD;IACvD,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,UAAU,CAAC;CACtB;AAED,2BAA2B;AAC3B,MAAM,WAAW,cAAc;IAC9B,GAAG,EAAE,OAAO,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;CACf;AAED;;;;;GAKG;AACH,wBAAsB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAMvE;AAED;;;;;GAKG;AACH,wBAAsB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAGrF;AAED;;;;;;GAMG;AACH,wBAAsB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,GAAE,MAAmB,GAAG,OAAO,CAAC,MAAM,CAAC,CAM5F;AAUD;;;;;;;GAOG;AACH,wBAAsB,WAAW,CAAC,KAAK,EAAE,gBAAgB,EAAE,OAAO,EAAE,eAAe,EAAE,IAAI,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,cAAc,CAAC,CAsMxI"}
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One-time rebuild of the ledger for the time before it existed.
|
|
3
|
+
*
|
|
4
|
+
* Sources: the token ledger (usage events) and the run traces (ticket roots,
|
|
5
|
+
* deliveries, busy turns, status changes). The traces expire after 90 days,
|
|
6
|
+
* the ledger does not, so this keeps what they know.
|
|
7
|
+
*
|
|
8
|
+
* Attribution of a usage event:
|
|
9
|
+
* - it already carries a trace id (the harness stamped it): `recorded`;
|
|
10
|
+
* - otherwise it goes to the trace the session's latest delivery named,
|
|
11
|
+
* when that delivery was at most {@link INFER_MAX_GAP_MS} before the event:
|
|
12
|
+
* `inferred`;
|
|
13
|
+
* - otherwise it lands in the agent's "(no ticket)" unit.
|
|
14
|
+
*
|
|
15
|
+
* Only facts older than the cutoff (the moment live recording began) are
|
|
16
|
+
* written, so history and live facts never overlap. The result goes to
|
|
17
|
+
* `backfill.jsonl`; the run is recorded in `state.json` and never repeats.
|
|
18
|
+
*
|
|
19
|
+
* @module services/usage-ledger/usage-ledger.backfill
|
|
20
|
+
*/
|
|
21
|
+
import { createReadStream, promises as fs } from 'fs';
|
|
22
|
+
import * as path from 'path';
|
|
23
|
+
import * as readline from 'readline';
|
|
24
|
+
import { TRACE_CONSTANTS } from '../../constants.js';
|
|
25
|
+
import { atomicWriteJson } from '../../utils/file-io.utils.js';
|
|
26
|
+
import { eventCostUsd, eventTokens } from '../monitoring/token-usage.service.js';
|
|
27
|
+
import { cleanText, cut, EXCERPT_MAX, titleFromSummary } from './excerpt.js';
|
|
28
|
+
import { LEDGER_VERSION, UNTICKETED_PREFIX } from './usage-ledger.types.js';
|
|
29
|
+
/** An unticketed event this soon after a delivery is credited to the delivery's trace. */
|
|
30
|
+
export const INFER_MAX_GAP_MS = TRACE_CONSTANTS.IDLE_CLEAR_MS;
|
|
31
|
+
/** File with the ledger's own bookkeeping. */
|
|
32
|
+
export const STATE_FILE = 'state.json';
|
|
33
|
+
/**
|
|
34
|
+
* Read the ledger bookkeeping.
|
|
35
|
+
*
|
|
36
|
+
* @param dir - Ledger directory
|
|
37
|
+
* @returns State (empty when none)
|
|
38
|
+
*/
|
|
39
|
+
export async function readLedgerState(dir) {
|
|
40
|
+
try {
|
|
41
|
+
return JSON.parse(await fs.readFile(path.join(dir, STATE_FILE), 'utf-8'));
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return {};
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Write the ledger bookkeeping.
|
|
49
|
+
*
|
|
50
|
+
* @param dir - Ledger directory
|
|
51
|
+
* @param state - State
|
|
52
|
+
*/
|
|
53
|
+
export async function writeLedgerState(dir, state) {
|
|
54
|
+
await fs.mkdir(dir, { recursive: true });
|
|
55
|
+
await atomicWriteJson(path.join(dir, STATE_FILE), state);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Note when live recording began (once).
|
|
59
|
+
*
|
|
60
|
+
* @param dir - Ledger directory
|
|
61
|
+
* @param now - Now (ms)
|
|
62
|
+
* @returns The cutoff
|
|
63
|
+
*/
|
|
64
|
+
export async function ensureLiveSince(dir, now = Date.now()) {
|
|
65
|
+
const state = await readLedgerState(dir);
|
|
66
|
+
if (state.liveSince)
|
|
67
|
+
return state.liveSince;
|
|
68
|
+
const liveSince = new Date(now).toISOString();
|
|
69
|
+
await writeLedgerState(dir, { ...state, liveSince });
|
|
70
|
+
return liveSince;
|
|
71
|
+
}
|
|
72
|
+
const hourOf = (ms) => new Date(Math.floor(ms / 3_600_000) * 3_600_000).toISOString();
|
|
73
|
+
/** Stream the lines of a file. */
|
|
74
|
+
async function* lines(file) {
|
|
75
|
+
const rl = readline.createInterface({ input: createReadStream(file, { encoding: 'utf-8' }), crlfDelay: Infinity });
|
|
76
|
+
for await (const line of rl)
|
|
77
|
+
yield line;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Rebuild history into `backfill.jsonl`.
|
|
81
|
+
*
|
|
82
|
+
* @param store - Ledger store
|
|
83
|
+
* @param sources - Where history comes from
|
|
84
|
+
* @param opts.cutoffMs - Only facts before this time are written
|
|
85
|
+
* @returns What happened
|
|
86
|
+
*/
|
|
87
|
+
export async function runBackfill(store, sources, opts) {
|
|
88
|
+
const state = await readLedgerState(store.dir);
|
|
89
|
+
if (state.backfill)
|
|
90
|
+
return { ran: false, reason: `already done at ${state.backfill.doneAt}`, facts: 0, usageEvents: 0, inferredEvents: 0, traces: 0 };
|
|
91
|
+
const cutoff = opts.cutoffMs;
|
|
92
|
+
const facts = [];
|
|
93
|
+
const push = (f) => {
|
|
94
|
+
facts.push(f);
|
|
95
|
+
};
|
|
96
|
+
// ---- traces ----
|
|
97
|
+
let index = null;
|
|
98
|
+
try {
|
|
99
|
+
index = JSON.parse(await fs.readFile(path.join(sources.traceDir, TRACE_CONSTANTS.INDEX_FILE), 'utf-8'));
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
index = null;
|
|
103
|
+
}
|
|
104
|
+
const traces = index?.traces ?? {};
|
|
105
|
+
const known = new Set(Object.keys(traces));
|
|
106
|
+
/** session → deliveries (ms, trace), sorted later */
|
|
107
|
+
const deliveries = new Map();
|
|
108
|
+
/** `session|ts` → traces that recorded the same busy turn */
|
|
109
|
+
const busyOwners = new Map();
|
|
110
|
+
const busyEvents = [];
|
|
111
|
+
const seenAgents = new Set();
|
|
112
|
+
const lastOwner = new Map();
|
|
113
|
+
for (const [traceId, entry] of Object.entries(traces)) {
|
|
114
|
+
const created = Date.parse(entry.root.createdAt);
|
|
115
|
+
if (created >= cutoff)
|
|
116
|
+
continue;
|
|
117
|
+
const root = entry.root;
|
|
118
|
+
push({
|
|
119
|
+
v: LEDGER_VERSION,
|
|
120
|
+
ts: root.createdAt,
|
|
121
|
+
kind: 'unit.meta',
|
|
122
|
+
unitId: traceId,
|
|
123
|
+
...(root.refs.ticketId ? { ticketId: root.refs.ticketId } : {}),
|
|
124
|
+
...(root.refs.requestId ? { requestId: root.refs.requestId } : {}),
|
|
125
|
+
rootKind: root.kind,
|
|
126
|
+
openedAt: root.createdAt,
|
|
127
|
+
title: titleFromSummary(root.summary),
|
|
128
|
+
excerpt: cut(cleanText(root.summary), EXCERPT_MAX),
|
|
129
|
+
attribution: 'recorded',
|
|
130
|
+
});
|
|
131
|
+
const file = path.join(sources.traceDir, `${traceId}.jsonl`);
|
|
132
|
+
try {
|
|
133
|
+
await fs.access(file);
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
for await (const line of lines(file)) {
|
|
139
|
+
let event;
|
|
140
|
+
try {
|
|
141
|
+
event = JSON.parse(line);
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
const at = Date.parse(event.ts);
|
|
147
|
+
if (!Number.isFinite(at) || at >= cutoff)
|
|
148
|
+
continue;
|
|
149
|
+
const session = event.refs?.session ?? event.actor?.session;
|
|
150
|
+
if (event.type === 'turn.delivered') {
|
|
151
|
+
if (session) {
|
|
152
|
+
const list = deliveries.get(session) ?? [];
|
|
153
|
+
list.push({ at, traceId });
|
|
154
|
+
deliveries.set(session, list);
|
|
155
|
+
const key = `${traceId}|${session}`;
|
|
156
|
+
if (!seenAgents.has(key)) {
|
|
157
|
+
seenAgents.add(key);
|
|
158
|
+
const info = sources.directory.lookup(session);
|
|
159
|
+
push({
|
|
160
|
+
v: LEDGER_VERSION,
|
|
161
|
+
ts: event.ts,
|
|
162
|
+
kind: 'unit.agent',
|
|
163
|
+
unitId: traceId,
|
|
164
|
+
agent: session,
|
|
165
|
+
...(info?.team ? { team: info.team } : {}),
|
|
166
|
+
...(info?.project ? { project: info.project } : {}),
|
|
167
|
+
attribution: 'recorded',
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
if (event.actor?.kind === 'owner' && event.data?.kind === 'owner_message') {
|
|
172
|
+
const last = lastOwner.get(traceId);
|
|
173
|
+
if (last === undefined || Math.abs(at - last) >= 2_000) {
|
|
174
|
+
lastOwner.set(traceId, at);
|
|
175
|
+
push({ v: LEDGER_VERSION, ts: event.ts, kind: 'owner.message', unitId: traceId, attribution: 'recorded' });
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
else if (event.type === 'turn.ended') {
|
|
180
|
+
const busyMs = Number(event.data?.busyMs);
|
|
181
|
+
if (session && Number.isFinite(busyMs) && busyMs > 0) {
|
|
182
|
+
busyEvents.push({ traceId, session, at, busyMs });
|
|
183
|
+
const key = `${session}|${event.ts}`;
|
|
184
|
+
busyOwners.set(key, [...(busyOwners.get(key) ?? []), traceId]);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
else if (event.type === 'request.status' || event.type === 'ticket.status') {
|
|
188
|
+
const to = typeof event.data?.to === 'string' ? event.data.to : '';
|
|
189
|
+
const projectId = typeof event.data?.projectId === 'string' ? event.data.projectId : '';
|
|
190
|
+
const project = projectId ? sources.directory.projectName(projectId) : undefined;
|
|
191
|
+
if (project)
|
|
192
|
+
push({ v: LEDGER_VERSION, ts: event.ts, kind: 'unit.meta', unitId: traceId, project, attribution: 'recorded' });
|
|
193
|
+
if (to === 'done' || to === 'cancelled')
|
|
194
|
+
push({ v: LEDGER_VERSION, ts: event.ts, kind: 'unit.closed', unitId: traceId, status: to, attribution: 'recorded' });
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
for (const list of deliveries.values())
|
|
199
|
+
list.sort((a, b) => a.at - b.at);
|
|
200
|
+
// ---- busy time ----
|
|
201
|
+
const busy = new Map();
|
|
202
|
+
for (const b of busyEvents) {
|
|
203
|
+
const share = 1 / (busyOwners.get(`${b.session}|${new Date(b.at).toISOString()}`)?.length ?? 1);
|
|
204
|
+
const hour = hourOf(b.at);
|
|
205
|
+
const key = [b.traceId, b.session, hour, share].join('|');
|
|
206
|
+
const info = sources.directory.lookup(b.session);
|
|
207
|
+
const fact = busy.get(key) ??
|
|
208
|
+
{ v: LEDGER_VERSION, ts: hour, kind: 'busy', unitId: b.traceId, agent: b.session, ...(info?.team ? { team: info.team } : {}), turns: 0, busyMs: 0, share, attribution: 'recorded' };
|
|
209
|
+
fact.turns += 1;
|
|
210
|
+
fact.busyMs += b.busyMs;
|
|
211
|
+
busy.set(key, fact);
|
|
212
|
+
}
|
|
213
|
+
facts.push(...busy.values());
|
|
214
|
+
// ---- usage ----
|
|
215
|
+
const usage = new Map();
|
|
216
|
+
const noticket = new Set();
|
|
217
|
+
let usageEvents = 0;
|
|
218
|
+
let inferredEvents = 0;
|
|
219
|
+
sources.forEachUsage((session, event) => {
|
|
220
|
+
const at = Date.parse(event.timestamp);
|
|
221
|
+
if (!Number.isFinite(at) || at >= cutoff)
|
|
222
|
+
return;
|
|
223
|
+
usageEvents += 1;
|
|
224
|
+
let unitId;
|
|
225
|
+
let attribution = 'recorded';
|
|
226
|
+
if (event.traceId && known.has(event.traceId)) {
|
|
227
|
+
unitId = event.traceId;
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
const list = deliveries.get(session) ?? [];
|
|
231
|
+
let found;
|
|
232
|
+
for (let i = list.length - 1; i >= 0; i--) {
|
|
233
|
+
if (list[i].at <= at) {
|
|
234
|
+
found = list[i];
|
|
235
|
+
break;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
if (found && at - found.at <= INFER_MAX_GAP_MS) {
|
|
239
|
+
unitId = found.traceId;
|
|
240
|
+
attribution = 'inferred';
|
|
241
|
+
inferredEvents += 1;
|
|
242
|
+
}
|
|
243
|
+
else {
|
|
244
|
+
unitId = `${UNTICKETED_PREFIX}${session}`;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
if (unitId.startsWith(UNTICKETED_PREFIX) && !noticket.has(unitId)) {
|
|
248
|
+
noticket.add(unitId);
|
|
249
|
+
push({ v: LEDGER_VERSION, ts: new Date(at).toISOString(), kind: 'unit.meta', unitId, rootKind: 'none', title: `(no ticket) ${session}`, attribution: 'inferred' });
|
|
250
|
+
}
|
|
251
|
+
const hour = hourOf(at);
|
|
252
|
+
const key = [unitId, session, event.model, hour, attribution].join('|');
|
|
253
|
+
const info = sources.directory.lookup(session);
|
|
254
|
+
const tokens = eventTokens(event);
|
|
255
|
+
const fact = usage.get(key) ??
|
|
256
|
+
{
|
|
257
|
+
v: LEDGER_VERSION,
|
|
258
|
+
ts: hour,
|
|
259
|
+
kind: 'usage',
|
|
260
|
+
unitId,
|
|
261
|
+
agent: session,
|
|
262
|
+
...(info?.team ? { team: info.team } : {}),
|
|
263
|
+
model: event.model,
|
|
264
|
+
...(event.runtime ? { runtime: event.runtime } : {}),
|
|
265
|
+
events: 0,
|
|
266
|
+
share: 1,
|
|
267
|
+
input: 0,
|
|
268
|
+
cachedInput: 0,
|
|
269
|
+
output: 0,
|
|
270
|
+
cacheWrite: 0,
|
|
271
|
+
budget: 0,
|
|
272
|
+
costUsd: 0,
|
|
273
|
+
attribution,
|
|
274
|
+
};
|
|
275
|
+
fact.events += 1;
|
|
276
|
+
fact.input += tokens.input;
|
|
277
|
+
fact.cachedInput += tokens.cachedInput;
|
|
278
|
+
fact.output += tokens.output;
|
|
279
|
+
fact.cacheWrite += tokens.cacheWrite;
|
|
280
|
+
fact.budget += tokens.budget;
|
|
281
|
+
fact.costUsd += eventCostUsd(event);
|
|
282
|
+
usage.set(key, fact);
|
|
283
|
+
});
|
|
284
|
+
facts.push(...usage.values());
|
|
285
|
+
for (let i = 0; i < facts.length; i += 5000)
|
|
286
|
+
store.appendMany(facts.slice(i, i + 5000), 'backfill');
|
|
287
|
+
await store.flush();
|
|
288
|
+
await writeLedgerState(store.dir, {
|
|
289
|
+
...(await readLedgerState(store.dir)),
|
|
290
|
+
backfill: { doneAt: new Date().toISOString(), cutoff: new Date(cutoff).toISOString(), facts: facts.length, usageEvents, inferredEvents },
|
|
291
|
+
});
|
|
292
|
+
return { ran: true, facts: facts.length, usageEvents, inferredEvents, traces: Object.keys(traces).length };
|
|
293
|
+
}
|
|
294
|
+
//# sourceMappingURL=usage-ledger.backfill.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usage-ledger.backfill.js","sourceRoot":"","sources":["../../../../../../backend/src/services/usage-ledger/usage-ledger.backfill.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAE,gBAAgB,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,IAAI,CAAC;AACtD,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,KAAK,QAAQ,MAAM,UAAU,CAAC;AACrC,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAC/D,OAAO,EAAE,YAAY,EAAE,WAAW,EAAwB,MAAM,sCAAsC,CAAC;AAEvG,OAAO,EAAE,SAAS,EAAE,GAAG,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAG7E,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAoE,MAAM,yBAAyB,CAAC;AAE9I,0FAA0F;AAC1F,MAAM,CAAC,MAAM,gBAAgB,GAAG,eAAe,CAAC,aAAa,CAAC;AAE9D,8CAA8C;AAC9C,MAAM,CAAC,MAAM,UAAU,GAAG,YAAY,CAAC;AA4BvC;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,GAAW;IAChD,IAAI,CAAC;QACJ,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,UAAU,CAAC,EAAE,OAAO,CAAC,CAAgB,CAAC;IAC1F,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,EAAE,CAAC;IACX,CAAC;AACF,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,GAAW,EAAE,KAAkB;IACrE,MAAM,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACzC,MAAM,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,UAAU,CAAC,EAAE,KAAK,CAAC,CAAC;AAC1D,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,GAAW,EAAE,MAAc,IAAI,CAAC,GAAG,EAAE;IAC1E,MAAM,KAAK,GAAG,MAAM,eAAe,CAAC,GAAG,CAAC,CAAC;IACzC,IAAI,KAAK,CAAC,SAAS;QAAE,OAAO,KAAK,CAAC,SAAS,CAAC;IAC5C,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC;IAC9C,MAAM,gBAAgB,CAAC,GAAG,EAAE,EAAE,GAAG,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC;IACrD,OAAO,SAAS,CAAC;AAClB,CAAC;AAED,MAAM,MAAM,GAAG,CAAC,EAAU,EAAU,EAAE,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,SAAS,CAAC,GAAG,SAAS,CAAC,CAAC,WAAW,EAAE,CAAC;AAEtG,kCAAkC;AAClC,KAAK,SAAS,CAAC,CAAC,KAAK,CAAC,IAAY;IACjC,MAAM,EAAE,GAAG,QAAQ,CAAC,eAAe,CAAC,EAAE,KAAK,EAAE,gBAAgB,CAAC,IAAI,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC,CAAC;IACnH,IAAI,KAAK,EAAE,MAAM,IAAI,IAAI,EAAE;QAAE,MAAM,IAAI,CAAC;AACzC,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,KAAuB,EAAE,OAAwB,EAAE,IAA0B;IAC9G,MAAM,KAAK,GAAG,MAAM,eAAe,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC/C,IAAI,KAAK,CAAC,QAAQ;QAAE,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,mBAAmB,KAAK,CAAC,QAAQ,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,cAAc,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;IACtJ,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC;IAC7B,MAAM,KAAK,GAAiB,EAAE,CAAC;IAC/B,MAAM,IAAI,GAAG,CAAC,CAAa,EAAQ,EAAE;QACpC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACf,CAAC,CAAC;IAEF,mBAAmB;IACnB,IAAI,KAAK,GAA0B,IAAI,CAAC;IACxC,IAAI,CAAC;QACJ,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,eAAe,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC,CAAmB,CAAC;IAC3H,CAAC;IAAC,MAAM,CAAC;QACR,KAAK,GAAG,IAAI,CAAC;IACd,CAAC;IACD,MAAM,MAAM,GAAG,KAAK,EAAE,MAAM,IAAI,EAAE,CAAC;IACnC,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;IAC3C,qDAAqD;IACrD,MAAM,UAAU,GAAG,IAAI,GAAG,EAAkD,CAAC;IAC7E,6DAA6D;IAC7D,MAAM,UAAU,GAAG,IAAI,GAAG,EAAoB,CAAC;IAC/C,MAAM,UAAU,GAA4E,EAAE,CAAC;IAC/F,MAAM,UAAU,GAAG,IAAI,GAAG,EAAU,CAAC;IACrC,MAAM,SAAS,GAAG,IAAI,GAAG,EAAkB,CAAC;IAE5C,KAAK,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QACvD,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACjD,IAAI,OAAO,IAAI,MAAM;YAAE,SAAS;QAChC,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC;QACxB,IAAI,CAAC;YACJ,CAAC,EAAE,cAAc;YACjB,EAAE,EAAE,IAAI,CAAC,SAAS;YAClB,IAAI,EAAE,WAAW;YACjB,MAAM,EAAE,OAAO;YACf,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC/D,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAClE,QAAQ,EAAE,IAAI,CAAC,IAAI;YACnB,QAAQ,EAAE,IAAI,CAAC,SAAS;YACxB,KAAK,EAAE,gBAAgB,CAAC,IAAI,CAAC,OAAO,CAAC;YACrC,OAAO,EAAE,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,WAAW,CAAC;YAClD,WAAW,EAAE,UAAU;SACvB,CAAC,CAAC;QACH,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,GAAG,OAAO,QAAQ,CAAC,CAAC;QAC7D,IAAI,CAAC;YACJ,MAAM,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACvB,CAAC;QAAC,MAAM,CAAC;YACR,SAAS;QACV,CAAC;QACD,IAAI,KAAK,EAAE,MAAM,IAAI,IAAI,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;YACtC,IAAI,KAAiB,CAAC;YACtB,IAAI,CAAC;gBACJ,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAe,CAAC;YACxC,CAAC;YAAC,MAAM,CAAC;gBACR,SAAS;YACV,CAAC;YACD,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;YAChC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,MAAM;gBAAE,SAAS;YACnD,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,OAAO,IAAI,KAAK,CAAC,KAAK,EAAE,OAAO,CAAC;YAC5D,IAAI,KAAK,CAAC,IAAI,KAAK,gBAAgB,EAAE,CAAC;gBACrC,IAAI,OAAO,EAAE,CAAC;oBACb,MAAM,IAAI,GAAG,UAAU,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;oBAC3C,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;oBAC3B,UAAU,CAAC,GAAG,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;oBAC9B,MAAM,GAAG,GAAG,GAAG,OAAO,IAAI,OAAO,EAAE,CAAC;oBACpC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;wBAC1B,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;wBACpB,MAAM,IAAI,GAAG,OAAO,CAAC,SAAS,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;wBAC/C,IAAI,CAAC;4BACJ,CAAC,EAAE,cAAc;4BACjB,EAAE,EAAE,KAAK,CAAC,EAAE;4BACZ,IAAI,EAAE,YAAY;4BAClB,MAAM,EAAE,OAAO;4BACf,KAAK,EAAE,OAAO;4BACd,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;4BAC1C,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;4BACnD,WAAW,EAAE,UAAU;yBACvB,CAAC,CAAC;oBACJ,CAAC;gBACF,CAAC;gBACD,IAAI,KAAK,CAAC,KAAK,EAAE,IAAI,KAAK,OAAO,IAAI,KAAK,CAAC,IAAI,EAAE,IAAI,KAAK,eAAe,EAAE,CAAC;oBAC3E,MAAM,IAAI,GAAG,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;oBACpC,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,CAAC,GAAG,CAAC,EAAE,GAAG,IAAI,CAAC,IAAI,KAAK,EAAE,CAAC;wBACxD,SAAS,CAAC,GAAG,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;wBAC3B,IAAI,CAAC,EAAE,CAAC,EAAE,cAAc,EAAE,EAAE,EAAE,KAAK,CAAC,EAAE,EAAE,IAAI,EAAE,eAAe,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,CAAC,CAAC;oBAC5G,CAAC;gBACF,CAAC;YACF,CAAC;iBAAM,IAAI,KAAK,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;gBACxC,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;gBAC1C,IAAI,OAAO,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,MAAM,GAAG,CAAC,EAAE,CAAC;oBACtD,UAAU,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;oBAClD,MAAM,GAAG,GAAG,GAAG,OAAO,IAAI,KAAK,CAAC,EAAE,EAAE,CAAC;oBACrC,UAAU,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;gBAChE,CAAC;YACF,CAAC;iBAAM,IAAI,KAAK,CAAC,IAAI,KAAK,gBAAgB,IAAI,KAAK,CAAC,IAAI,KAAK,eAAe,EAAE,CAAC;gBAC9E,MAAM,EAAE,GAAG,OAAO,KAAK,CAAC,IAAI,EAAE,EAAE,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACnE,MAAM,SAAS,GAAG,OAAO,KAAK,CAAC,IAAI,EAAE,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC;gBACxF,MAAM,OAAO,GAAG,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;gBACjF,IAAI,OAAO;oBAAE,IAAI,CAAC,EAAE,CAAC,EAAE,cAAc,EAAE,EAAE,EAAE,KAAK,CAAC,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,CAAC,CAAC;gBAC7H,IAAI,EAAE,KAAK,MAAM,IAAI,EAAE,KAAK,WAAW;oBAAE,IAAI,CAAC,EAAE,CAAC,EAAE,cAAc,EAAE,EAAE,EAAE,KAAK,CAAC,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,WAAW,EAAE,UAAU,EAAE,CAAC,CAAC;YAC/J,CAAC;QACF,CAAC;IACF,CAAC;IACD,KAAK,MAAM,IAAI,IAAI,UAAU,CAAC,MAAM,EAAE;QAAE,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC;IAEzE,sBAAsB;IACtB,MAAM,IAAI,GAAG,IAAI,GAAG,EAAoB,CAAC;IACzC,KAAK,MAAM,CAAC,IAAI,UAAU,EAAE,CAAC;QAC5B,MAAM,KAAK,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,OAAO,IAAI,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC,EAAE,MAAM,IAAI,CAAC,CAAC,CAAC;QAChG,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAC1B,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC1D,MAAM,IAAI,GAAG,OAAO,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;QACjD,MAAM,IAAI,GACT,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;YACZ,EAAE,CAAC,EAAE,cAAc,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC,OAAO,EAAE,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,UAAU,EAAsB,CAAC;QAC1M,IAAI,CAAC,KAAK,IAAI,CAAC,CAAC;QAChB,IAAI,CAAC,MAAM,IAAI,CAAC,CAAC,MAAM,CAAC;QACxB,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACrB,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;IAE7B,kBAAkB;IAClB,MAAM,KAAK,GAAG,IAAI,GAAG,EAAqB,CAAC;IAC3C,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAU,CAAC;IACnC,IAAI,WAAW,GAAG,CAAC,CAAC;IACpB,IAAI,cAAc,GAAG,CAAC,CAAC;IACvB,OAAO,CAAC,YAAY,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE;QACvC,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QACvC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,MAAM;YAAE,OAAO;QACjD,WAAW,IAAI,CAAC,CAAC;QACjB,IAAI,MAAc,CAAC;QACnB,IAAI,WAAW,GAAgB,UAAU,CAAC;QAC1C,IAAI,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;YAC/C,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC;QACxB,CAAC;aAAM,CAAC;YACP,MAAM,IAAI,GAAG,UAAU,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;YAC3C,IAAI,KAAkD,CAAC;YACvD,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;gBAC3C,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,CAAC;oBACtB,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;oBAChB,MAAM;gBACP,CAAC;YACF,CAAC;YACD,IAAI,KAAK,IAAI,EAAE,GAAG,KAAK,CAAC,EAAE,IAAI,gBAAgB,EAAE,CAAC;gBAChD,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC;gBACvB,WAAW,GAAG,UAAU,CAAC;gBACzB,cAAc,IAAI,CAAC,CAAC;YACrB,CAAC;iBAAM,CAAC;gBACP,MAAM,GAAG,GAAG,iBAAiB,GAAG,OAAO,EAAE,CAAC;YAC3C,CAAC;QACF,CAAC;QACD,IAAI,MAAM,CAAC,UAAU,CAAC,iBAAiB,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;YACnE,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YACrB,IAAI,CAAC,EAAE,CAAC,EAAE,cAAc,EAAE,EAAE,EAAE,IAAI,IAAI,CAAC,EAAE,CAAC,CAAC,WAAW,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,eAAe,OAAO,EAAE,EAAE,WAAW,EAAE,UAAU,EAAE,CAAC,CAAC;QACpK,CAAC;QACD,MAAM,IAAI,GAAG,MAAM,CAAC,EAAE,CAAC,CAAC;QACxB,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC,KAAK,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACxE,MAAM,IAAI,GAAG,OAAO,CAAC,SAAS,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAC/C,MAAM,MAAM,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC;QAClC,MAAM,IAAI,GACT,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC;YACb;gBACA,CAAC,EAAE,cAAc;gBACjB,EAAE,EAAE,IAAI;gBACR,IAAI,EAAE,OAAO;gBACb,MAAM;gBACN,KAAK,EAAE,OAAO;gBACd,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC1C,KAAK,EAAE,KAAK,CAAC,KAAK;gBAClB,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACpD,MAAM,EAAE,CAAC;gBACT,KAAK,EAAE,CAAC;gBACR,KAAK,EAAE,CAAC;gBACR,WAAW,EAAE,CAAC;gBACd,MAAM,EAAE,CAAC;gBACT,UAAU,EAAE,CAAC;gBACb,MAAM,EAAE,CAAC;gBACT,OAAO,EAAE,CAAC;gBACV,WAAW;aACU,CAAC;QACxB,IAAI,CAAC,MAAM,IAAI,CAAC,CAAC;QACjB,IAAI,CAAC,KAAK,IAAI,MAAM,CAAC,KAAK,CAAC;QAC3B,IAAI,CAAC,WAAW,IAAI,MAAM,CAAC,WAAW,CAAC;QACvC,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,CAAC;QAC7B,IAAI,CAAC,UAAU,IAAI,MAAM,CAAC,UAAU,CAAC;QACrC,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,CAAC;QAC7B,IAAI,CAAC,OAAO,IAAI,YAAY,CAAC,KAAK,CAAC,CAAC;QACpC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACtB,CAAC,CAAC,CAAC;IACH,KAAK,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;IAE9B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,IAAI,IAAI;QAAE,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,EAAE,UAAU,CAAC,CAAC;IACpG,MAAM,KAAK,CAAC,KAAK,EAAE,CAAC;IACpB,MAAM,gBAAgB,CAAC,KAAK,CAAC,GAAG,EAAE;QACjC,GAAG,CAAC,MAAM,eAAe,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACrC,QAAQ,EAAE,EAAE,MAAM,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,MAAM,EAAE,IAAI,IAAI,CAAC,MAAM,CAAC,CAAC,WAAW,EAAE,EAAE,KAAK,EAAE,KAAK,CAAC,MAAM,EAAE,WAAW,EAAE,cAAc,EAAE;KACxI,CAAC,CAAC;IACH,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,MAAM,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,CAAC;AAC5G,CAAC"}
|