@ai-dossier/cli 0.51.0 → 0.53.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/README.md +38 -2
  2. package/dist/batch-suite-runner.d.ts +30 -6
  3. package/dist/batch-suite-runner.d.ts.map +1 -1
  4. package/dist/batch-suite-runner.js +86 -25
  5. package/dist/batch-suite-runner.js.map +1 -1
  6. package/dist/capability.d.ts +8 -0
  7. package/dist/capability.d.ts.map +1 -1
  8. package/dist/capability.js +16 -1
  9. package/dist/capability.js.map +1 -1
  10. package/dist/cli.js +2 -0
  11. package/dist/cli.js.map +1 -1
  12. package/dist/commands/run.d.ts.map +1 -1
  13. package/dist/commands/run.js +10 -1
  14. package/dist/commands/run.js.map +1 -1
  15. package/dist/commands/sched.d.ts.map +1 -1
  16. package/dist/commands/sched.js +35 -0
  17. package/dist/commands/sched.js.map +1 -1
  18. package/dist/commands/usage.d.ts +84 -0
  19. package/dist/commands/usage.d.ts.map +1 -0
  20. package/dist/commands/usage.js +395 -0
  21. package/dist/commands/usage.js.map +1 -0
  22. package/dist/usage/collect-claude.d.ts +47 -0
  23. package/dist/usage/collect-claude.d.ts.map +1 -0
  24. package/dist/usage/collect-claude.js +237 -0
  25. package/dist/usage/collect-claude.js.map +1 -0
  26. package/dist/usage/collect-dispatch.d.ts +73 -0
  27. package/dist/usage/collect-dispatch.d.ts.map +1 -0
  28. package/dist/usage/collect-dispatch.js +297 -0
  29. package/dist/usage/collect-dispatch.js.map +1 -0
  30. package/dist/usage/collect-opencode.d.ts +74 -0
  31. package/dist/usage/collect-opencode.d.ts.map +1 -0
  32. package/dist/usage/collect-opencode.js +198 -0
  33. package/dist/usage/collect-opencode.js.map +1 -0
  34. package/dist/usage/host-session.d.ts +63 -0
  35. package/dist/usage/host-session.d.ts.map +1 -0
  36. package/dist/usage/host-session.js +325 -0
  37. package/dist/usage/host-session.js.map +1 -0
  38. package/dist/usage/ledger.d.ts +87 -0
  39. package/dist/usage/ledger.d.ts.map +1 -0
  40. package/dist/usage/ledger.js +268 -0
  41. package/dist/usage/ledger.js.map +1 -0
  42. package/dist/usage/types.d.ts +80 -0
  43. package/dist/usage/types.d.ts.map +1 -0
  44. package/dist/usage/types.js +11 -0
  45. package/dist/usage/types.js.map +1 -0
  46. package/dist/usage/util.d.ts +36 -0
  47. package/dist/usage/util.d.ts.map +1 -0
  48. package/dist/usage/util.js +158 -0
  49. package/dist/usage/util.js.map +1 -0
  50. package/package.json +2 -2
@@ -0,0 +1,74 @@
1
+ /**
2
+ * opencode collector (#769): per-message usage from opencode's SQLite store
3
+ * (`~/.local/share/opencode/opencode.db`, `message.data` JSON with
4
+ * `modelID`/`providerID`/`tokens`/`cost`).
5
+ *
6
+ * Read-only by construction: the database is opened with `readOnly: true`.
7
+ * The store can be very large (14 GB observed on hcc) and `message` has no
8
+ * time index, so the query never scans `message` by time — it selects the
9
+ * (small) `session` table by `time_updated`, then reads each session's
10
+ * messages through the `(session_id, time_created, id)` index.
11
+ *
12
+ * Uses the built-in `node:sqlite` (Node >= 22.13 unflagged). Where it is
13
+ * unavailable (Node 20) the collector reports `unavailable` rather than
14
+ * failing the whole view.
15
+ */
16
+ import type { UsageRow } from './types';
17
+ /** Session metadata the collector needs. */
18
+ export interface OpenCodeSession {
19
+ id: string;
20
+ parent_id: string | null;
21
+ directory: string | null;
22
+ title: string | null;
23
+ }
24
+ /** One `message` row as stored (`data` is the JSON payload). */
25
+ export interface OpenCodeMessage {
26
+ id: string;
27
+ time_created: number;
28
+ data: string;
29
+ }
30
+ /** The narrow read surface the collector uses — injectable for tests. */
31
+ export interface OpenCodeReader {
32
+ sessionsUpdatedSince(sinceMs: number): OpenCodeSession[];
33
+ messagesSince(sessionId: string, sinceMs: number): OpenCodeMessage[];
34
+ close(): void;
35
+ }
36
+ export type OpenCodeOpenResult = {
37
+ status: 'ok';
38
+ reader: OpenCodeReader;
39
+ } | {
40
+ status: 'unavailable' | 'error';
41
+ detail: string;
42
+ };
43
+ /** Default opencode DB path, honouring `OPENCODE_DB` and `XDG_DATA_HOME`. */
44
+ export declare function defaultOpenCodeDbPath(env?: NodeJS.ProcessEnv): string;
45
+ interface SqliteStatement {
46
+ all(...params: unknown[]): unknown[];
47
+ }
48
+ interface SqliteDatabase {
49
+ prepare(sql: string): SqliteStatement;
50
+ close(): void;
51
+ }
52
+ type SqliteModule = {
53
+ DatabaseSync: new (file: string, options?: {
54
+ readOnly?: boolean;
55
+ }) => SqliteDatabase;
56
+ };
57
+ /** `node:sqlite`, or null on a runtime without it. */
58
+ export declare function loadNodeSqlite(): SqliteModule | null;
59
+ /** Open `dbFile` read-only. Never throws. */
60
+ export declare function openOpenCodeDb(dbFile: string, sqlite?: SqliteModule | null): OpenCodeOpenResult;
61
+ export interface OpenCodeCollectOptions {
62
+ reader: OpenCodeReader;
63
+ sinceMs: number;
64
+ untilMs?: number;
65
+ host: string;
66
+ }
67
+ /**
68
+ * Usage rows for every assistant message with non-zero tokens in sessions
69
+ * active since `sinceMs`. A message still streaming (all-zero tokens) is
70
+ * skipped rather than recorded as a zero row.
71
+ */
72
+ export declare function collectOpenCode(opts: OpenCodeCollectOptions): UsageRow[];
73
+ export {};
74
+ //# sourceMappingURL=collect-opencode.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collect-opencode.d.ts","sourceRoot":"","sources":["../../src/usage/collect-opencode.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAKH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAGxC,4CAA4C;AAC5C,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAED,gEAAgE;AAChE,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,yEAAyE;AACzE,MAAM,WAAW,cAAc;IAC7B,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,eAAe,EAAE,CAAC;IACzD,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,eAAe,EAAE,CAAC;IACrE,KAAK,IAAI,IAAI,CAAC;CACf;AAED,MAAM,MAAM,kBAAkB,GAC1B;IAAE,MAAM,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,cAAc,CAAA;CAAE,GACxC;IAAE,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAExD,6EAA6E;AAC7E,wBAAgB,qBAAqB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,CAIlF;AAKD,UAAU,eAAe;IACvB,GAAG,CAAC,GAAG,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,EAAE,CAAC;CACtC;AACD,UAAU,cAAc;IACtB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,eAAe,CAAC;IACtC,KAAK,IAAI,IAAI,CAAC;CACf;AACD,KAAK,YAAY,GAAG;IAClB,YAAY,EAAE,KAAK,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,KAAK,cAAc,CAAC;CACtF,CAAC;AAEF,sDAAsD;AACtD,wBAAgB,cAAc,IAAI,YAAY,GAAG,IAAI,CAoBpD;AAED,6CAA6C;AAC7C,wBAAgB,cAAc,CAC5B,MAAM,EAAE,MAAM,EACd,MAAM,GAAE,YAAY,GAAG,IAAuB,GAC7C,kBAAkB,CAwCpB;AAED,MAAM,WAAW,sBAAsB;IACrC,MAAM,EAAE,cAAc,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,sBAAsB,GAAG,QAAQ,EAAE,CAyDxE"}
@@ -0,0 +1,198 @@
1
+ "use strict";
2
+ /**
3
+ * opencode collector (#769): per-message usage from opencode's SQLite store
4
+ * (`~/.local/share/opencode/opencode.db`, `message.data` JSON with
5
+ * `modelID`/`providerID`/`tokens`/`cost`).
6
+ *
7
+ * Read-only by construction: the database is opened with `readOnly: true`.
8
+ * The store can be very large (14 GB observed on hcc) and `message` has no
9
+ * time index, so the query never scans `message` by time — it selects the
10
+ * (small) `session` table by `time_updated`, then reads each session's
11
+ * messages through the `(session_id, time_created, id)` index.
12
+ *
13
+ * Uses the built-in `node:sqlite` (Node >= 22.13 unflagged). Where it is
14
+ * unavailable (Node 20) the collector reports `unavailable` rather than
15
+ * failing the whole view.
16
+ */
17
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
18
+ if (k2 === undefined) k2 = k;
19
+ var desc = Object.getOwnPropertyDescriptor(m, k);
20
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
21
+ desc = { enumerable: true, get: function() { return m[k]; } };
22
+ }
23
+ Object.defineProperty(o, k2, desc);
24
+ }) : (function(o, m, k, k2) {
25
+ if (k2 === undefined) k2 = k;
26
+ o[k2] = m[k];
27
+ }));
28
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
29
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
30
+ }) : function(o, v) {
31
+ o["default"] = v;
32
+ });
33
+ var __importStar = (this && this.__importStar) || (function () {
34
+ var ownKeys = function(o) {
35
+ ownKeys = Object.getOwnPropertyNames || function (o) {
36
+ var ar = [];
37
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
38
+ return ar;
39
+ };
40
+ return ownKeys(o);
41
+ };
42
+ return function (mod) {
43
+ if (mod && mod.__esModule) return mod;
44
+ var result = {};
45
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
46
+ __setModuleDefault(result, mod);
47
+ return result;
48
+ };
49
+ })();
50
+ Object.defineProperty(exports, "__esModule", { value: true });
51
+ exports.defaultOpenCodeDbPath = defaultOpenCodeDbPath;
52
+ exports.loadNodeSqlite = loadNodeSqlite;
53
+ exports.openOpenCodeDb = openOpenCodeDb;
54
+ exports.collectOpenCode = collectOpenCode;
55
+ const fs = __importStar(require("node:fs"));
56
+ const os = __importStar(require("node:os"));
57
+ const path = __importStar(require("node:path"));
58
+ const util_1 = require("./util");
59
+ /** Default opencode DB path, honouring `OPENCODE_DB` and `XDG_DATA_HOME`. */
60
+ function defaultOpenCodeDbPath(env = process.env) {
61
+ if (env.OPENCODE_DB)
62
+ return env.OPENCODE_DB;
63
+ const dataHome = env.XDG_DATA_HOME || path.join(os.homedir(), '.local', 'share');
64
+ return path.join(dataHome, 'opencode', 'opencode.db');
65
+ }
66
+ /** How far before the window start messages are read (a long-running message completes later than it is created). */
67
+ const MESSAGE_SLACK_MS = 60 * 60 * 1000;
68
+ /** `node:sqlite`, or null on a runtime without it. */
69
+ function loadNodeSqlite() {
70
+ try {
71
+ // Lazy: a static import would crash the whole CLI on Node 20.
72
+ // Node 22.13–22.x still emits an ExperimentalWarning on first load; it
73
+ // would land on stderr of every `ai-dossier run` inside opencode. Drop
74
+ // just that one warning while loading.
75
+ const emit = process.emitWarning;
76
+ process.emitWarning = ((warning, ...rest) => {
77
+ const text = typeof warning === 'string' ? warning : warning.message;
78
+ if (/sqlite/i.test(text))
79
+ return;
80
+ return emit.call(process, warning, ...rest);
81
+ });
82
+ try {
83
+ return require('node:sqlite');
84
+ }
85
+ finally {
86
+ process.emitWarning = emit;
87
+ }
88
+ }
89
+ catch {
90
+ return null;
91
+ }
92
+ }
93
+ /** Open `dbFile` read-only. Never throws. */
94
+ function openOpenCodeDb(dbFile, sqlite = loadNodeSqlite()) {
95
+ if (!fs.existsSync(dbFile))
96
+ return { status: 'unavailable', detail: `no database at ${dbFile}` };
97
+ if (!sqlite) {
98
+ return {
99
+ status: 'unavailable',
100
+ detail: `node:sqlite not available in Node ${process.versions.node} (needs >= 22.13)`,
101
+ };
102
+ }
103
+ let db;
104
+ try {
105
+ db = new sqlite.DatabaseSync(dbFile, { readOnly: true });
106
+ }
107
+ catch (err) {
108
+ return { status: 'error', detail: `cannot open ${dbFile}: ${err.message}` };
109
+ }
110
+ let sessions;
111
+ let messages;
112
+ try {
113
+ sessions = db.prepare('SELECT id, parent_id, directory, title FROM session WHERE time_updated >= ?');
114
+ // `time_created` bound is widened by MESSAGE_SLACK_MS: a row's timestamp is
115
+ // its COMPLETION time, so a message created just before the window and
116
+ // completed inside it must still be read; the collector re-filters exactly.
117
+ messages = db.prepare('SELECT id, time_created, data FROM message WHERE session_id = ? AND time_created >= ? ORDER BY time_created');
118
+ }
119
+ catch (err) {
120
+ // Not an opencode store of the expected shape (other version / wrong file).
121
+ db.close();
122
+ return { status: 'error', detail: `unexpected schema in ${dbFile}: ${err.message}` };
123
+ }
124
+ return {
125
+ status: 'ok',
126
+ reader: {
127
+ sessionsUpdatedSince: (sinceMs) => sessions.all(sinceMs),
128
+ messagesSince: (sessionId, sinceMs) => messages.all(sessionId, sinceMs - MESSAGE_SLACK_MS),
129
+ close: () => db.close(),
130
+ },
131
+ };
132
+ }
133
+ /**
134
+ * Usage rows for every assistant message with non-zero tokens in sessions
135
+ * active since `sinceMs`. A message still streaming (all-zero tokens) is
136
+ * skipped rather than recorded as a zero row.
137
+ */
138
+ function collectOpenCode(opts) {
139
+ const rows = [];
140
+ const untilMs = opts.untilMs ?? Number.POSITIVE_INFINITY;
141
+ for (const session of opts.reader.sessionsUpdatedSince(opts.sinceMs)) {
142
+ for (const message of opts.reader.messagesSince(session.id, opts.sinceMs)) {
143
+ let data;
144
+ try {
145
+ data = JSON.parse(message.data);
146
+ }
147
+ catch {
148
+ continue;
149
+ }
150
+ if (data.role !== 'assistant')
151
+ continue;
152
+ const tokens = (data.tokens ?? {});
153
+ const cache = (tokens.cache ?? {});
154
+ const input = (0, util_1.count)(tokens.input);
155
+ const output = (0, util_1.count)(tokens.output);
156
+ const reasoning = (0, util_1.count)(tokens.reasoning);
157
+ const cache_read = (0, util_1.count)(cache.read);
158
+ const cache_write = (0, util_1.count)(cache.write);
159
+ if (input + output + reasoning + cache_read + cache_write === 0)
160
+ continue;
161
+ const time = (data.time ?? {});
162
+ const tsMs = typeof time.completed === 'number' ? time.completed : message.time_created;
163
+ if (tsMs < opts.sinceMs || tsMs >= untilMs)
164
+ continue;
165
+ const pathInfo = (data.path ?? {});
166
+ const cwd = typeof pathInfo.cwd === 'string' ? pathInfo.cwd : session.directory;
167
+ const providerID = typeof data.providerID === 'string' ? data.providerID : null;
168
+ const modelID = typeof data.modelID === 'string' ? data.modelID : null;
169
+ const issue = (0, util_1.issueFromRef)(cwd ? path.basename(cwd) : null);
170
+ rows.push({
171
+ ts: new Date(tsMs).toISOString(),
172
+ host: opts.host,
173
+ provider: providerID ?? 'opencode',
174
+ // `provider/model` — the same id shape opencode's own `-m` flag takes.
175
+ model: modelID ? (providerID ? `${providerID}/${modelID}` : modelID) : null,
176
+ input,
177
+ output,
178
+ reasoning,
179
+ cache_read,
180
+ cache_write,
181
+ cost_usd: typeof data.cost === 'number' ? data.cost : null,
182
+ source: 'opencode',
183
+ session_id: session.id,
184
+ parent_session_id: session.parent_id ?? null,
185
+ title: session.title,
186
+ cwd,
187
+ project: (0, util_1.projectOf)(cwd),
188
+ branch: null,
189
+ issue,
190
+ issue_source: issue === null ? null : 'branch',
191
+ batch: null,
192
+ unit: null,
193
+ });
194
+ }
195
+ }
196
+ return rows;
197
+ }
198
+ //# sourceMappingURL=collect-opencode.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collect-opencode.js","sourceRoot":"","sources":["../../src/usage/collect-opencode.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Resolve the agent host an `ai-dossier run` was invoked from — session id
3
+ * and the model it is running — so `runs.jsonl` records a concrete model
4
+ * instead of null (#769, "fix at the source").
5
+ *
6
+ * ~99% of `runs.jsonl` rows are `llm: auto` invocations made BY a host agent
7
+ * (Claude Code, opencode) that then follows the printed dossier itself; no
8
+ * agent is spawned, so nothing reported a model. The host's own store knows
9
+ * it:
10
+ *
11
+ * - Claude Code exports `CLAUDE_CODE_SESSION_ID`; its transcript
12
+ * (`~/.claude/projects/<slug>/<id>.jsonl`, or a subagent's under
13
+ * `<id>/subagents/`) records each assistant message's resolved `model`.
14
+ * Subagents share the parent's id, so the caller is the transcript whose
15
+ * newest tool call is a still-pending `ai-dossier run` (else the recently
16
+ * active transcripts' model when they all agree; null when they don't).
17
+ * - Both markers are inherited by child processes, so the NEAREST host
18
+ * ancestor (`CLAUDE_PID` / `OPENCODE_PID` via /proc) wins.
19
+ * - opencode exports `OPENCODE=1` but no session id; the newest session for
20
+ * this working directory in opencode.db (updated in the last few minutes)
21
+ * is the caller.
22
+ *
23
+ * Best effort and bounded: every failure yields nulls, never an exception.
24
+ */
25
+ export interface HostSession {
26
+ agent: 'claude-code' | 'opencode' | null;
27
+ session_id: string | null;
28
+ model: string | null;
29
+ }
30
+ /** The last real (non-synthetic) assistant `message.model` in a transcript's tail. */
31
+ export declare function lastAssistantModel(transcript: string): string | null;
32
+ /** Newest transcript file (main or subagent) belonging to `sessionId`, or null. */
33
+ export declare function findClaudeTranscript(projectsDir: string, sessionId: string): string | null;
34
+ /**
35
+ * Whether a transcript's newest tool call is a still-pending `ai-dossier run`
36
+ * — i.e. this transcript's agent is the one executing us right now: the
37
+ * command is in a `tool_use` block whose `tool_result` has not been written.
38
+ */
39
+ export declare function hasPendingDossierRun(transcript: string): boolean;
40
+ /**
41
+ * The model of the Claude Code agent invoking us. Subagents share their
42
+ * parent's `CLAUDE_CODE_SESSION_ID`, so "newest transcript" alone can pick a
43
+ * concurrent sibling: prefer the one transcript with a pending `ai-dossier
44
+ * run` call; otherwise accept the recently-active candidates' model only when
45
+ * they all agree — a guess between different models records null.
46
+ */
47
+ export declare function claudeHostModel(projectsDir: string, sessionId: string): string | null;
48
+ /**
49
+ * How many generations up `pid` is from this process: a number when it is a
50
+ * proven ancestor, null when it provably is not (an inherited env marker from
51
+ * an unrelated outer host), UNKNOWN_DEPTH when it cannot be told.
52
+ */
53
+ export declare function ancestorDepth(pidText: string | undefined, self?: number): number | null;
54
+ export interface DetectHostOptions {
55
+ env?: NodeJS.ProcessEnv;
56
+ cwd?: string;
57
+ nowMs?: number;
58
+ claudeProjectsDir?: string;
59
+ opencodeDb?: string;
60
+ }
61
+ /** Identify the invoking agent host, its session, and its current model. Never throws. */
62
+ export declare function detectHostSession(opts?: DetectHostOptions): HostSession;
63
+ //# sourceMappingURL=host-session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"host-session.d.ts","sourceRoot":"","sources":["../../src/usage/host-session.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAQH,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,aAAa,GAAG,UAAU,GAAG,IAAI,CAAC;IACzC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AA2BD,sFAAsF;AACtF,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAqBpE;AAED,mFAAmF;AACnF,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAgB1F;AA4BD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CA+BhE;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAWrF;AAkBD;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,MAAM,GAAG,SAAS,EAC3B,IAAI,GAAE,MAAoB,GACzB,MAAM,GAAG,IAAI,CASf;AA2CD,MAAM,WAAW,iBAAiB;IAChC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,0FAA0F;AAC1F,wBAAgB,iBAAiB,CAAC,IAAI,GAAE,iBAAsB,GAAG,WAAW,CAiC3E"}