@aaac/observability 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +54 -0
- package/dist/chunk-FVFGVBXM.js +1307 -0
- package/dist/chunk-FVFGVBXM.js.map +1 -0
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.js +425 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/index.d.ts +959 -0
- package/dist/index.js +63 -0
- package/dist/index.js.map +1 -0
- package/package.json +52 -0
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
DEFAULT_DB_PATH,
|
|
4
|
+
SqliteQueryAdapter,
|
|
5
|
+
createPipeline,
|
|
6
|
+
generateId,
|
|
7
|
+
isoToUnixNano
|
|
8
|
+
} from "../chunk-FVFGVBXM.js";
|
|
9
|
+
|
|
10
|
+
// src/cli/index.ts
|
|
11
|
+
import { readFileSync } from "fs";
|
|
12
|
+
import { dirname, resolve } from "path";
|
|
13
|
+
import { fileURLToPath } from "url";
|
|
14
|
+
|
|
15
|
+
// src/generated/program.ts
|
|
16
|
+
import { Command } from "commander";
|
|
17
|
+
|
|
18
|
+
// src/generated/contract.ts
|
|
19
|
+
var CONTRACT_YAML = `# yaml-language-server: $schema=./node_modules/cli-contracts/schemas/cli-contract.schema.json
|
|
20
|
+
cli_contracts: 0.1.0
|
|
21
|
+
|
|
22
|
+
info:
|
|
23
|
+
title: AaaC Observability CLI
|
|
24
|
+
version: 0.1.0
|
|
25
|
+
description: aaac-observ \u2014 external event registration and query for @aaac/observability
|
|
26
|
+
|
|
27
|
+
command_sets:
|
|
28
|
+
aaac-observ:
|
|
29
|
+
summary: aaac-observ \u2014 register external events and query the observability store
|
|
30
|
+
executable: aaac-observ
|
|
31
|
+
commands:
|
|
32
|
+
record:
|
|
33
|
+
summary: Register an external event into the observability pipeline (thin entrypoint for EventCollector.registerExternalEvent)
|
|
34
|
+
options:
|
|
35
|
+
- name: event-type
|
|
36
|
+
aliases: [t]
|
|
37
|
+
schema: { type: string }
|
|
38
|
+
description: "Event type (e.g. promotion.commit, process.edit, agent.session)"
|
|
39
|
+
- name: lifecycle
|
|
40
|
+
aliases: [l]
|
|
41
|
+
schema:
|
|
42
|
+
type: string
|
|
43
|
+
enum: [open, close, event, instant]
|
|
44
|
+
description: "Event lifecycle phase: open | close | event | instant"
|
|
45
|
+
- name: span-id
|
|
46
|
+
aliases: [s]
|
|
47
|
+
schema: { type: string }
|
|
48
|
+
description: Span ID (auto-generated if omitted)
|
|
49
|
+
- name: parent-span-id
|
|
50
|
+
schema: { type: string }
|
|
51
|
+
description: Parent span ID for hierarchy
|
|
52
|
+
- name: session-id
|
|
53
|
+
schema: { type: string }
|
|
54
|
+
description: Session ID to associate with the event
|
|
55
|
+
- name: trace-id
|
|
56
|
+
schema: { type: string }
|
|
57
|
+
description: Trace ID for distributed tracing
|
|
58
|
+
- name: source
|
|
59
|
+
schema: { type: string }
|
|
60
|
+
description: "Event source identifier (default: external)"
|
|
61
|
+
- name: attr
|
|
62
|
+
schema:
|
|
63
|
+
type: array
|
|
64
|
+
items: { type: string }
|
|
65
|
+
description: "Attribute as key=value (repeatable, e.g. --attr git.commit=abc123)"
|
|
66
|
+
- name: link
|
|
67
|
+
schema:
|
|
68
|
+
type: array
|
|
69
|
+
items: { type: string }
|
|
70
|
+
description: "Cross-axis link as targetSpanId:linkType[:targetTraceId] (repeatable)"
|
|
71
|
+
- name: db
|
|
72
|
+
schema: { type: string }
|
|
73
|
+
description: Path to observability SQLite database (overrides default)
|
|
74
|
+
exits:
|
|
75
|
+
'0':
|
|
76
|
+
description: Event registered successfully
|
|
77
|
+
stdout:
|
|
78
|
+
format: json
|
|
79
|
+
schema:
|
|
80
|
+
type: object
|
|
81
|
+
properties:
|
|
82
|
+
eventId:
|
|
83
|
+
type: string
|
|
84
|
+
spanId:
|
|
85
|
+
type: string
|
|
86
|
+
'1':
|
|
87
|
+
description: Registration failed (validation error or write error)
|
|
88
|
+
stderr:
|
|
89
|
+
format: text
|
|
90
|
+
|
|
91
|
+
query:
|
|
92
|
+
summary: Query the observability store via QueryAdapter
|
|
93
|
+
options:
|
|
94
|
+
- name: kind
|
|
95
|
+
aliases: [k]
|
|
96
|
+
schema:
|
|
97
|
+
type: string
|
|
98
|
+
enum: [trace, span, search, links]
|
|
99
|
+
description: "Query kind: trace | span | search | links"
|
|
100
|
+
- name: trace-id
|
|
101
|
+
schema: { type: string }
|
|
102
|
+
description: Trace ID for trace queries (--kind trace)
|
|
103
|
+
- name: span-id
|
|
104
|
+
aliases: [s]
|
|
105
|
+
schema: { type: string }
|
|
106
|
+
description: Span ID for span/links queries (--kind span or --kind links)
|
|
107
|
+
- name: event-type
|
|
108
|
+
schema: { type: string }
|
|
109
|
+
description: Event type filter for search queries (--kind search)
|
|
110
|
+
- name: task-id
|
|
111
|
+
schema: { type: string }
|
|
112
|
+
description: Task ID filter for search queries (--kind search)
|
|
113
|
+
- name: from
|
|
114
|
+
schema: { type: string }
|
|
115
|
+
description: Start time in ISO 8601 format for search queries (--kind search)
|
|
116
|
+
- name: to
|
|
117
|
+
schema: { type: string }
|
|
118
|
+
description: End time in ISO 8601 format for search queries (--kind search)
|
|
119
|
+
- name: direction
|
|
120
|
+
aliases: [d]
|
|
121
|
+
schema:
|
|
122
|
+
type: string
|
|
123
|
+
enum: [forward, reverse, both]
|
|
124
|
+
description: "Link traversal direction for links queries (default: both)"
|
|
125
|
+
- name: db
|
|
126
|
+
schema: { type: string }
|
|
127
|
+
description: Path to observability SQLite database (overrides default)
|
|
128
|
+
exits:
|
|
129
|
+
'0':
|
|
130
|
+
description: Query succeeded \u2014 results as JSON array
|
|
131
|
+
stdout:
|
|
132
|
+
format: json
|
|
133
|
+
schema:
|
|
134
|
+
type: object
|
|
135
|
+
'1':
|
|
136
|
+
description: Query failed
|
|
137
|
+
stderr:
|
|
138
|
+
format: text
|
|
139
|
+
`;
|
|
140
|
+
var CONTRACT_JSON_STR = '{\n "cli_contracts": "0.1.0",\n "info": {\n "title": "AaaC Observability CLI",\n "version": "0.1.0",\n "description": "aaac-observ \u2014 external event registration and query for @aaac/observability"\n },\n "command_sets": {\n "aaac-observ": {\n "summary": "aaac-observ \u2014 register external events and query the observability store",\n "executable": "aaac-observ",\n "commands": {\n "record": {\n "summary": "Register an external event into the observability pipeline (thin entrypoint for EventCollector.registerExternalEvent)",\n "options": [\n {\n "name": "event-type",\n "aliases": [\n "t"\n ],\n "schema": {\n "type": "string"\n },\n "description": "Event type (e.g. promotion.commit, process.edit, agent.session)"\n },\n {\n "name": "lifecycle",\n "aliases": [\n "l"\n ],\n "schema": {\n "type": "string",\n "enum": [\n "open",\n "close",\n "event",\n "instant"\n ]\n },\n "description": "Event lifecycle phase: open | close | event | instant"\n },\n {\n "name": "span-id",\n "aliases": [\n "s"\n ],\n "schema": {\n "type": "string"\n },\n "description": "Span ID (auto-generated if omitted)"\n },\n {\n "name": "parent-span-id",\n "schema": {\n "type": "string"\n },\n "description": "Parent span ID for hierarchy"\n },\n {\n "name": "session-id",\n "schema": {\n "type": "string"\n },\n "description": "Session ID to associate with the event"\n },\n {\n "name": "trace-id",\n "schema": {\n "type": "string"\n },\n "description": "Trace ID for distributed tracing"\n },\n {\n "name": "source",\n "schema": {\n "type": "string"\n },\n "description": "Event source identifier (default: external)"\n },\n {\n "name": "attr",\n "schema": {\n "type": "array",\n "items": {\n "type": "string"\n }\n },\n "description": "Attribute as key=value (repeatable, e.g. --attr git.commit=abc123)"\n },\n {\n "name": "link",\n "schema": {\n "type": "array",\n "items": {\n "type": "string"\n }\n },\n "description": "Cross-axis link as targetSpanId:linkType[:targetTraceId] (repeatable)"\n },\n {\n "name": "db",\n "schema": {\n "type": "string"\n },\n "description": "Path to observability SQLite database (overrides default)"\n }\n ],\n "exits": {\n "0": {\n "description": "Event registered successfully",\n "stdout": {\n "format": "json",\n "schema": {\n "type": "object",\n "properties": {\n "eventId": {\n "type": "string"\n },\n "spanId": {\n "type": "string"\n }\n }\n }\n }\n },\n "1": {\n "description": "Registration failed (validation error or write error)",\n "stderr": {\n "format": "text"\n }\n }\n }\n },\n "query": {\n "summary": "Query the observability store via QueryAdapter",\n "options": [\n {\n "name": "kind",\n "aliases": [\n "k"\n ],\n "schema": {\n "type": "string",\n "enum": [\n "trace",\n "span",\n "search",\n "links"\n ]\n },\n "description": "Query kind: trace | span | search | links"\n },\n {\n "name": "trace-id",\n "schema": {\n "type": "string"\n },\n "description": "Trace ID for trace queries (--kind trace)"\n },\n {\n "name": "span-id",\n "aliases": [\n "s"\n ],\n "schema": {\n "type": "string"\n },\n "description": "Span ID for span/links queries (--kind span or --kind links)"\n },\n {\n "name": "event-type",\n "schema": {\n "type": "string"\n },\n "description": "Event type filter for search queries (--kind search)"\n },\n {\n "name": "task-id",\n "schema": {\n "type": "string"\n },\n "description": "Task ID filter for search queries (--kind search)"\n },\n {\n "name": "from",\n "schema": {\n "type": "string"\n },\n "description": "Start time in ISO 8601 format for search queries (--kind search)"\n },\n {\n "name": "to",\n "schema": {\n "type": "string"\n },\n "description": "End time in ISO 8601 format for search queries (--kind search)"\n },\n {\n "name": "direction",\n "aliases": [\n "d"\n ],\n "schema": {\n "type": "string",\n "enum": [\n "forward",\n "reverse",\n "both"\n ]\n },\n "description": "Link traversal direction for links queries (default: both)"\n },\n {\n "name": "db",\n "schema": {\n "type": "string"\n },\n "description": "Path to observability SQLite database (overrides default)"\n }\n ],\n "exits": {\n "0": {\n "description": "Query succeeded \u2014 results as JSON array",\n "stdout": {\n "format": "json",\n "schema": {\n "type": "object"\n }\n }\n },\n "1": {\n "description": "Query failed",\n "stderr": {\n "format": "text"\n }\n }\n }\n }\n }\n }\n }\n}';
|
|
141
|
+
|
|
142
|
+
// src/generated/program.ts
|
|
143
|
+
function createProgram(handlers2, version) {
|
|
144
|
+
const program2 = new Command();
|
|
145
|
+
program2.name("aaac-observ").version(version, "-V, --version").description("aaac-observ \u2014 register external events and query the observability store");
|
|
146
|
+
program2.command("record").description("Register an external event into the observability pipeline (thin entrypoint for EventCollector.registerExternalEvent)").option("-t, --event-type <value>", "Event type (e.g. promotion.commit, process.edit, agent.session)").option("-l, --lifecycle <value>", "Event lifecycle phase: open | close | event | instant").option("-s, --span-id <value>", "Span ID (auto-generated if omitted)").option("--parent-span-id <value>", "Parent span ID for hierarchy").option("--session-id <value>", "Session ID to associate with the event").option("--trace-id <value>", "Trace ID for distributed tracing").option("--source <value>", "Event source identifier (default: external)").option("--attr <value>", "Attribute as key=value (repeatable, e.g. --attr git.commit=abc123)").option("--link <value>", "Cross-axis link as targetSpanId:linkType[:targetTraceId] (repeatable)").option("--db <value>", "Path to observability SQLite database (overrides default)").action(async (opts, cmd) => {
|
|
147
|
+
await handlers2.record(opts, cmd.optsWithGlobals());
|
|
148
|
+
});
|
|
149
|
+
program2.command("query").description("Query the observability store via QueryAdapter").option("-k, --kind <value>", "Query kind: trace | span | search | links").option("--trace-id <value>", "Trace ID for trace queries (--kind trace)").option("-s, --span-id <value>", "Span ID for span/links queries (--kind span or --kind links)").option("--event-type <value>", "Event type filter for search queries (--kind search)").option("--task-id <value>", "Task ID filter for search queries (--kind search)").option("--from <value>", "Start time in ISO 8601 format for search queries (--kind search)").option("--to <value>", "End time in ISO 8601 format for search queries (--kind search)").option("-d, --direction <value>", "Link traversal direction for links queries (default: both)").option("--db <value>", "Path to observability SQLite database (overrides default)").action(async (opts, cmd) => {
|
|
150
|
+
await handlers2.query(opts, cmd.optsWithGlobals());
|
|
151
|
+
});
|
|
152
|
+
program2.command("extract").description("Extract contract specification for this CLI tool.").argument("[commands...]", "Command IDs to extract. Use dot notation.").option("-a, --all", "Extract all commands.", false).option("--include-meta", "Include extraction metadata.", true).option("-F, --format <format>", "Output format (yaml or json).", "yaml").action(async (commands, opts, cmd) => {
|
|
153
|
+
if (commands.length === 0 && !opts.all) {
|
|
154
|
+
process.stderr.write(JSON.stringify({ code: "INVALID_ARGS", message: "Specify command IDs or use --all" }) + "\n");
|
|
155
|
+
process.exit(2);
|
|
156
|
+
}
|
|
157
|
+
const format = opts.format || "yaml";
|
|
158
|
+
const doc = JSON.parse(CONTRACT_JSON_STR);
|
|
159
|
+
const cmdIds = opts.all ? [] : commands;
|
|
160
|
+
if (cmdIds.length === 0) {
|
|
161
|
+
if (format === "json") {
|
|
162
|
+
const out = {};
|
|
163
|
+
if (opts.includeMeta) {
|
|
164
|
+
out._meta = {
|
|
165
|
+
source: "embedded",
|
|
166
|
+
type: "cli-contracts/extract",
|
|
167
|
+
extractedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
168
|
+
specVersion: doc.cli_contracts ?? "0.1.0",
|
|
169
|
+
commands: ["aaac-observ.record", "aaac-observ.query"]
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
Object.assign(out, doc);
|
|
173
|
+
process.stdout.write(JSON.stringify(out, null, 2) + "\n");
|
|
174
|
+
} else {
|
|
175
|
+
const yamlLines = [];
|
|
176
|
+
yamlLines.push("# aaac-observ extract");
|
|
177
|
+
yamlLines.push("# source: embedded");
|
|
178
|
+
yamlLines.push("# type: cli-contracts/command-extract");
|
|
179
|
+
if (opts.includeMeta) {
|
|
180
|
+
yamlLines.push("---");
|
|
181
|
+
yamlLines.push("source: embedded");
|
|
182
|
+
yamlLines.push("type: cli-contracts/command-extract");
|
|
183
|
+
yamlLines.push("extractedAt: " + (/* @__PURE__ */ new Date()).toISOString());
|
|
184
|
+
yamlLines.push("spec_version: " + (doc.cli_contracts ?? "0.1.0"));
|
|
185
|
+
yamlLines.push("commands:");
|
|
186
|
+
for (const id of ["aaac-observ.record", "aaac-observ.query"]) {
|
|
187
|
+
yamlLines.push(" - " + id);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
yamlLines.push("---");
|
|
191
|
+
yamlLines.push(CONTRACT_YAML);
|
|
192
|
+
process.stdout.write(yamlLines.join("\n") + "\n");
|
|
193
|
+
}
|
|
194
|
+
} else {
|
|
195
|
+
const filtered = {
|
|
196
|
+
cli_contracts: doc.cli_contracts,
|
|
197
|
+
info: doc.info,
|
|
198
|
+
command_sets: {}
|
|
199
|
+
};
|
|
200
|
+
const fcs = filtered.command_sets;
|
|
201
|
+
for (const [setId, cs] of Object.entries(doc.command_sets ?? {})) {
|
|
202
|
+
const cmds = cs.commands;
|
|
203
|
+
if (!cmds) continue;
|
|
204
|
+
const matched = {};
|
|
205
|
+
for (const [cmdId, cmdDef] of Object.entries(cmds)) {
|
|
206
|
+
const fullId = setId + "." + cmdId;
|
|
207
|
+
if (cmdIds.some((id) => id === cmdId || id === fullId || cmdId.startsWith(id + "."))) {
|
|
208
|
+
matched[cmdId] = cmdDef;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
if (Object.keys(matched).length > 0) {
|
|
212
|
+
const setCopy = { ...cs };
|
|
213
|
+
setCopy.commands = matched;
|
|
214
|
+
fcs[setId] = setCopy;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
if (doc.components) filtered.components = doc.components;
|
|
218
|
+
process.stdout.write(JSON.stringify(filtered, null, 2) + "\n");
|
|
219
|
+
}
|
|
220
|
+
process.exit(0);
|
|
221
|
+
});
|
|
222
|
+
return program2;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// src/cli/record.ts
|
|
226
|
+
function parseAttr(attrStr) {
|
|
227
|
+
if (!attrStr) return {};
|
|
228
|
+
const trimmed = attrStr.trim();
|
|
229
|
+
if (trimmed.startsWith("{")) {
|
|
230
|
+
try {
|
|
231
|
+
return JSON.parse(trimmed);
|
|
232
|
+
} catch {
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
const result = {};
|
|
236
|
+
for (const pair of trimmed.split(",")) {
|
|
237
|
+
const eqIdx = pair.indexOf("=");
|
|
238
|
+
if (eqIdx > 0) {
|
|
239
|
+
const key = pair.slice(0, eqIdx).trim();
|
|
240
|
+
const val = pair.slice(eqIdx + 1).trim();
|
|
241
|
+
result[key] = val;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
return result;
|
|
245
|
+
}
|
|
246
|
+
function parseLinks(linkStr) {
|
|
247
|
+
if (!linkStr) return [];
|
|
248
|
+
const links = [];
|
|
249
|
+
for (const item of linkStr.split(",")) {
|
|
250
|
+
const parts = item.trim().split(":");
|
|
251
|
+
if (parts.length >= 2) {
|
|
252
|
+
const targetSpanId = parts[0].trim();
|
|
253
|
+
const linkType = parts[1].trim();
|
|
254
|
+
const targetTraceId = parts[2]?.trim() || void 0;
|
|
255
|
+
if (targetSpanId && linkType) {
|
|
256
|
+
links.push({ targetSpanId, linkType, targetTraceId });
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
return links;
|
|
261
|
+
}
|
|
262
|
+
async function handleRecord(options, _parentOpts) {
|
|
263
|
+
if (!options.eventType) {
|
|
264
|
+
process.stderr.write("Error: --event-type (-t) is required\n");
|
|
265
|
+
process.exit(1);
|
|
266
|
+
}
|
|
267
|
+
const validLifecycles = ["open", "close", "event", "instant"];
|
|
268
|
+
if (!options.lifecycle) {
|
|
269
|
+
process.stderr.write(
|
|
270
|
+
`Error: --lifecycle (-l) is required (${validLifecycles.join("|")})
|
|
271
|
+
`
|
|
272
|
+
);
|
|
273
|
+
process.exit(1);
|
|
274
|
+
}
|
|
275
|
+
if (!validLifecycles.includes(options.lifecycle)) {
|
|
276
|
+
process.stderr.write(
|
|
277
|
+
`Error: --lifecycle must be one of: ${validLifecycles.join("|")}
|
|
278
|
+
`
|
|
279
|
+
);
|
|
280
|
+
process.exit(1);
|
|
281
|
+
}
|
|
282
|
+
const dbPath = options.db ?? DEFAULT_DB_PATH;
|
|
283
|
+
const spanId = options.spanId ?? generateId();
|
|
284
|
+
const attributes = parseAttr(options.attr);
|
|
285
|
+
const links = parseLinks(options.link);
|
|
286
|
+
if (options.sessionId) {
|
|
287
|
+
attributes["session_id"] = options.sessionId;
|
|
288
|
+
}
|
|
289
|
+
if (options.traceId) {
|
|
290
|
+
attributes["trace_id"] = options.traceId;
|
|
291
|
+
}
|
|
292
|
+
let capturedEventId;
|
|
293
|
+
const { collector, sink } = createPipeline({
|
|
294
|
+
dbPath,
|
|
295
|
+
// Use afterCorrelate to capture the event id before it is written
|
|
296
|
+
afterCorrelate: (event) => {
|
|
297
|
+
capturedEventId = event.id;
|
|
298
|
+
return event;
|
|
299
|
+
},
|
|
300
|
+
// Disable enrichment rules for external CLI events to keep the write
|
|
301
|
+
// path lightweight (no Enricher cache, no artifact lookups, no derived events)
|
|
302
|
+
enrichRules: []
|
|
303
|
+
});
|
|
304
|
+
try {
|
|
305
|
+
collector.registerExternalEvent({
|
|
306
|
+
source: options.source ?? "external",
|
|
307
|
+
eventType: options.eventType,
|
|
308
|
+
lifecycle: options.lifecycle,
|
|
309
|
+
spanId,
|
|
310
|
+
parentSpanId: options.parentSpanId,
|
|
311
|
+
attributes,
|
|
312
|
+
links
|
|
313
|
+
});
|
|
314
|
+
} catch (err) {
|
|
315
|
+
process.stderr.write(
|
|
316
|
+
`Error: ${err instanceof Error ? err.message : String(err)}
|
|
317
|
+
`
|
|
318
|
+
);
|
|
319
|
+
sink.close();
|
|
320
|
+
process.exit(1);
|
|
321
|
+
}
|
|
322
|
+
sink.close();
|
|
323
|
+
const result = { eventId: capturedEventId, spanId };
|
|
324
|
+
process.stdout.write(JSON.stringify(result) + "\n");
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// src/cli/query.ts
|
|
328
|
+
function bigIntReplacer(_key, value) {
|
|
329
|
+
return typeof value === "bigint" ? value.toString() : value;
|
|
330
|
+
}
|
|
331
|
+
async function handleQuery(options, _parentOpts) {
|
|
332
|
+
const validKinds = ["trace", "span", "search", "links"];
|
|
333
|
+
if (!options.kind) {
|
|
334
|
+
process.stderr.write(
|
|
335
|
+
`Error: --kind (-k) is required (${validKinds.join("|")})
|
|
336
|
+
`
|
|
337
|
+
);
|
|
338
|
+
process.exit(1);
|
|
339
|
+
}
|
|
340
|
+
if (!validKinds.includes(options.kind)) {
|
|
341
|
+
process.stderr.write(
|
|
342
|
+
`Error: --kind must be one of: ${validKinds.join("|")}
|
|
343
|
+
`
|
|
344
|
+
);
|
|
345
|
+
process.exit(1);
|
|
346
|
+
}
|
|
347
|
+
const kind = options.kind;
|
|
348
|
+
if (kind === "trace" && !options.traceId) {
|
|
349
|
+
process.stderr.write("Error: --trace-id is required for --kind trace\n");
|
|
350
|
+
process.exit(1);
|
|
351
|
+
}
|
|
352
|
+
if (kind === "span" && !options.spanId) {
|
|
353
|
+
process.stderr.write("Error: --span-id is required for --kind span\n");
|
|
354
|
+
process.exit(1);
|
|
355
|
+
}
|
|
356
|
+
if (kind === "links" && !options.spanId) {
|
|
357
|
+
process.stderr.write("Error: --span-id is required for --kind links\n");
|
|
358
|
+
process.exit(1);
|
|
359
|
+
}
|
|
360
|
+
const dbPath = options.db ?? DEFAULT_DB_PATH;
|
|
361
|
+
const adapter = new SqliteQueryAdapter(dbPath);
|
|
362
|
+
try {
|
|
363
|
+
let result;
|
|
364
|
+
switch (kind) {
|
|
365
|
+
// ── trace: all spans in a trace ─────────────────────────────────────
|
|
366
|
+
case "trace": {
|
|
367
|
+
result = adapter.getTrace(options.traceId);
|
|
368
|
+
break;
|
|
369
|
+
}
|
|
370
|
+
// ── span: single span + its events ──────────────────────────────────
|
|
371
|
+
case "span": {
|
|
372
|
+
const span = adapter.getSpan(options.spanId);
|
|
373
|
+
const events = adapter.getSpanEvents(options.spanId);
|
|
374
|
+
result = span !== void 0 ? { span, events } : null;
|
|
375
|
+
break;
|
|
376
|
+
}
|
|
377
|
+
// ── search: spans matching event-type / task-id / time range ─────────
|
|
378
|
+
case "search": {
|
|
379
|
+
const filter = {};
|
|
380
|
+
if (options.eventType) filter.eventType = options.eventType;
|
|
381
|
+
if (options.taskId) filter.taskId = options.taskId;
|
|
382
|
+
if (options.from) filter.fromTimeUnixNano = isoToUnixNano(options.from);
|
|
383
|
+
if (options.to) filter.toTimeUnixNano = isoToUnixNano(options.to);
|
|
384
|
+
result = adapter.querySpans(filter);
|
|
385
|
+
break;
|
|
386
|
+
}
|
|
387
|
+
// ── links: link traversal ─────────────────────────────────────────────
|
|
388
|
+
case "links": {
|
|
389
|
+
const validDirections = ["forward", "reverse", "both"];
|
|
390
|
+
const dir = options.direction !== void 0 && validDirections.includes(options.direction) ? options.direction : "both";
|
|
391
|
+
result = adapter.getLinks(options.spanId, dir);
|
|
392
|
+
break;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
process.stdout.write(JSON.stringify(result, bigIntReplacer) + "\n");
|
|
396
|
+
} catch (err) {
|
|
397
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
398
|
+
if (msg.includes("no such table") || msg.includes("no such column")) {
|
|
399
|
+
const emptyResult = kind === "span" ? null : [];
|
|
400
|
+
process.stdout.write(JSON.stringify(emptyResult, bigIntReplacer) + "\n");
|
|
401
|
+
adapter.close();
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
process.stderr.write(`Error: ${msg}
|
|
405
|
+
`);
|
|
406
|
+
adapter.close();
|
|
407
|
+
process.exit(1);
|
|
408
|
+
}
|
|
409
|
+
adapter.close();
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
// src/cli/handlers.ts
|
|
413
|
+
var handlers = {
|
|
414
|
+
record: handleRecord,
|
|
415
|
+
query: handleQuery
|
|
416
|
+
};
|
|
417
|
+
|
|
418
|
+
// src/cli/index.ts
|
|
419
|
+
var __dirname = dirname(fileURLToPath(import.meta.url));
|
|
420
|
+
var pkg = JSON.parse(
|
|
421
|
+
readFileSync(resolve(__dirname, "../../package.json"), "utf8")
|
|
422
|
+
);
|
|
423
|
+
var program = createProgram(handlers, pkg.version);
|
|
424
|
+
await program.parseAsync();
|
|
425
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/cli/index.ts","../../src/generated/program.ts","../../src/generated/contract.ts","../../src/cli/record.ts","../../src/cli/query.ts","../../src/cli/handlers.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * aaac-observ CLI entry point.\n *\n * Reads the version from package.json, creates the generated Commander\n * program with hand-written handlers, and executes it.\n */\nimport { readFileSync } from \"node:fs\";\nimport { dirname, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { createProgram } from \"../generated/program.js\";\nimport { handlers } from \"./handlers.js\";\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst pkg = JSON.parse(\n readFileSync(resolve(__dirname, \"../../package.json\"), \"utf8\"),\n) as { version: string };\n\nconst program = createProgram(handlers, pkg.version);\nawait program.parseAsync();\n","// Auto-generated by cli-contracts. Do not edit.\nimport { Command } from \"commander\";\nimport { CONTRACT_YAML, CONTRACT_JSON_STR } from \"./contract.js\";\n\nexport interface CommandHandlers {\n record: (options: { eventType?: string; lifecycle?: string; spanId?: string; parentSpanId?: string; sessionId?: string; traceId?: string; source?: string; attr?: string; link?: string; db?: string }, parentOpts: Record<string, unknown>) => Promise<void>;\n query: (options: { kind?: string; traceId?: string; spanId?: string; eventType?: string; taskId?: string; from?: string; to?: string; direction?: string; db?: string }, parentOpts: Record<string, unknown>) => Promise<void>;\n}\n\nexport function createProgram(\n handlers: CommandHandlers,\n version: string,\n): Command {\n const program = new Command();\n program\n .name(\"aaac-observ\")\n .version(version, \"-V, --version\")\n .description(\"aaac-observ — register external events and query the observability store\");\n\n\n program\n .command(\"record\")\n .description(\"Register an external event into the observability pipeline (thin entrypoint for EventCollector.registerExternalEvent)\")\n .option(\"-t, --event-type <value>\", \"Event type (e.g. promotion.commit, process.edit, agent.session)\")\n .option(\"-l, --lifecycle <value>\", \"Event lifecycle phase: open | close | event | instant\")\n .option(\"-s, --span-id <value>\", \"Span ID (auto-generated if omitted)\")\n .option(\"--parent-span-id <value>\", \"Parent span ID for hierarchy\")\n .option(\"--session-id <value>\", \"Session ID to associate with the event\")\n .option(\"--trace-id <value>\", \"Trace ID for distributed tracing\")\n .option(\"--source <value>\", \"Event source identifier (default: external)\")\n .option(\"--attr <value>\", \"Attribute as key=value (repeatable, e.g. --attr git.commit=abc123)\")\n .option(\"--link <value>\", \"Cross-axis link as targetSpanId:linkType[:targetTraceId] (repeatable)\")\n .option(\"--db <value>\", \"Path to observability SQLite database (overrides default)\")\n .action(async (opts, cmd) => {\n await handlers.record(opts, cmd.optsWithGlobals());\n });\n\n program\n .command(\"query\")\n .description(\"Query the observability store via QueryAdapter\")\n .option(\"-k, --kind <value>\", \"Query kind: trace | span | search | links\")\n .option(\"--trace-id <value>\", \"Trace ID for trace queries (--kind trace)\")\n .option(\"-s, --span-id <value>\", \"Span ID for span/links queries (--kind span or --kind links)\")\n .option(\"--event-type <value>\", \"Event type filter for search queries (--kind search)\")\n .option(\"--task-id <value>\", \"Task ID filter for search queries (--kind search)\")\n .option(\"--from <value>\", \"Start time in ISO 8601 format for search queries (--kind search)\")\n .option(\"--to <value>\", \"End time in ISO 8601 format for search queries (--kind search)\")\n .option(\"-d, --direction <value>\", \"Link traversal direction for links queries (default: both)\")\n .option(\"--db <value>\", \"Path to observability SQLite database (overrides default)\")\n .action(async (opts, cmd) => {\n await handlers.query(opts, cmd.optsWithGlobals());\n });\n\n\n // Built-in extract command (auto-injected by cli-contracts)\n program\n .command(\"extract\")\n .description(\"Extract contract specification for this CLI tool.\")\n .argument(\"[commands...]\", \"Command IDs to extract. Use dot notation.\")\n .option(\"-a, --all\", \"Extract all commands.\", false)\n .option(\"--include-meta\", \"Include extraction metadata.\", true)\n .option(\"-F, --format <format>\", \"Output format (yaml or json).\", \"yaml\")\n .action(async (commands: string[], opts: { all?: boolean; includeMeta?: boolean; format?: string }, cmd: Command) => {\n if (commands.length === 0 && !opts.all) {\n process.stderr.write(JSON.stringify({ code: \"INVALID_ARGS\", message: \"Specify command IDs or use --all\" }) + \"\\n\");\n process.exit(2);\n }\n\n const format = opts.format || \"yaml\";\n const doc = JSON.parse(CONTRACT_JSON_STR);\n const cmdIds = opts.all ? [] : commands;\n\n if (cmdIds.length === 0) {\n // --all: output full contract\n if (format === \"json\") {\n const out: Record<string, unknown> = {};\n if (opts.includeMeta) {\n out._meta = {\n source: \"embedded\",\n type: \"cli-contracts/extract\",\n extractedAt: new Date().toISOString(),\n specVersion: doc.cli_contracts ?? \"0.1.0\",\n commands: [\"aaac-observ.record\",\"aaac-observ.query\"],\n };\n }\n Object.assign(out, doc);\n process.stdout.write(JSON.stringify(out, null, 2) + \"\\n\");\n } else {\n // YAML output\n const yamlLines: string[] = [];\n yamlLines.push(\"# aaac-observ extract\");\n yamlLines.push(\"# source: embedded\");\n yamlLines.push(\"# type: cli-contracts/command-extract\");\n if (opts.includeMeta) {\n yamlLines.push(\"---\");\n yamlLines.push(\"source: embedded\");\n yamlLines.push(\"type: cli-contracts/command-extract\");\n yamlLines.push(\"extractedAt: \" + new Date().toISOString());\n yamlLines.push(\"spec_version: \" + (doc.cli_contracts ?? \"0.1.0\"));\n yamlLines.push(\"commands:\");\n for (const id of [\"aaac-observ.record\",\"aaac-observ.query\"]) {\n yamlLines.push(\" - \" + id);\n }\n }\n yamlLines.push(\"---\");\n yamlLines.push(CONTRACT_YAML);\n process.stdout.write(yamlLines.join(\"\\n\") + \"\\n\");\n }\n } else {\n // Filter specific commands\n const filtered: Record<string, unknown> = {\n cli_contracts: doc.cli_contracts,\n info: doc.info,\n command_sets: {},\n };\n const fcs = filtered.command_sets as Record<string, Record<string, unknown>>;\n for (const [setId, cs] of Object.entries(doc.command_sets ?? {})) {\n const cmds = (cs as Record<string, unknown>).commands as Record<string, unknown> | undefined;\n if (!cmds) continue;\n const matched: Record<string, unknown> = {};\n for (const [cmdId, cmdDef] of Object.entries(cmds)) {\n const fullId = setId + \".\" + cmdId;\n if (cmdIds.some((id) => id === cmdId || id === fullId || cmdId.startsWith(id + \".\"))) {\n matched[cmdId] = cmdDef;\n }\n }\n if (Object.keys(matched).length > 0) {\n const setCopy = { ...(cs as Record<string, unknown>) };\n setCopy.commands = matched;\n fcs[setId] = setCopy;\n }\n }\n if (doc.components) filtered.components = doc.components;\n process.stdout.write(JSON.stringify(filtered, null, 2) + \"\\n\");\n }\n process.exit(0);\n });\n return program;\n}\n","// Auto-generated by cli-contracts. Do not edit.\n// Embedded contract for the extract subcommand.\n\nexport const CONTRACT_YAML: string = \"# yaml-language-server: $schema=./node_modules/cli-contracts/schemas/cli-contract.schema.json\\ncli_contracts: 0.1.0\\n\\ninfo:\\n title: AaaC Observability CLI\\n version: 0.1.0\\n description: aaac-observ — external event registration and query for @aaac/observability\\n\\ncommand_sets:\\n aaac-observ:\\n summary: aaac-observ — register external events and query the observability store\\n executable: aaac-observ\\n commands:\\n record:\\n summary: Register an external event into the observability pipeline (thin entrypoint for EventCollector.registerExternalEvent)\\n options:\\n - name: event-type\\n aliases: [t]\\n schema: { type: string }\\n description: \\\"Event type (e.g. promotion.commit, process.edit, agent.session)\\\"\\n - name: lifecycle\\n aliases: [l]\\n schema:\\n type: string\\n enum: [open, close, event, instant]\\n description: \\\"Event lifecycle phase: open | close | event | instant\\\"\\n - name: span-id\\n aliases: [s]\\n schema: { type: string }\\n description: Span ID (auto-generated if omitted)\\n - name: parent-span-id\\n schema: { type: string }\\n description: Parent span ID for hierarchy\\n - name: session-id\\n schema: { type: string }\\n description: Session ID to associate with the event\\n - name: trace-id\\n schema: { type: string }\\n description: Trace ID for distributed tracing\\n - name: source\\n schema: { type: string }\\n description: \\\"Event source identifier (default: external)\\\"\\n - name: attr\\n schema:\\n type: array\\n items: { type: string }\\n description: \\\"Attribute as key=value (repeatable, e.g. --attr git.commit=abc123)\\\"\\n - name: link\\n schema:\\n type: array\\n items: { type: string }\\n description: \\\"Cross-axis link as targetSpanId:linkType[:targetTraceId] (repeatable)\\\"\\n - name: db\\n schema: { type: string }\\n description: Path to observability SQLite database (overrides default)\\n exits:\\n '0':\\n description: Event registered successfully\\n stdout:\\n format: json\\n schema:\\n type: object\\n properties:\\n eventId:\\n type: string\\n spanId:\\n type: string\\n '1':\\n description: Registration failed (validation error or write error)\\n stderr:\\n format: text\\n\\n query:\\n summary: Query the observability store via QueryAdapter\\n options:\\n - name: kind\\n aliases: [k]\\n schema:\\n type: string\\n enum: [trace, span, search, links]\\n description: \\\"Query kind: trace | span | search | links\\\"\\n - name: trace-id\\n schema: { type: string }\\n description: Trace ID for trace queries (--kind trace)\\n - name: span-id\\n aliases: [s]\\n schema: { type: string }\\n description: Span ID for span/links queries (--kind span or --kind links)\\n - name: event-type\\n schema: { type: string }\\n description: Event type filter for search queries (--kind search)\\n - name: task-id\\n schema: { type: string }\\n description: Task ID filter for search queries (--kind search)\\n - name: from\\n schema: { type: string }\\n description: Start time in ISO 8601 format for search queries (--kind search)\\n - name: to\\n schema: { type: string }\\n description: End time in ISO 8601 format for search queries (--kind search)\\n - name: direction\\n aliases: [d]\\n schema:\\n type: string\\n enum: [forward, reverse, both]\\n description: \\\"Link traversal direction for links queries (default: both)\\\"\\n - name: db\\n schema: { type: string }\\n description: Path to observability SQLite database (overrides default)\\n exits:\\n '0':\\n description: Query succeeded — results as JSON array\\n stdout:\\n format: json\\n schema:\\n type: object\\n '1':\\n description: Query failed\\n stderr:\\n format: text\\n\";\n\nexport const CONTRACT_JSON_STR: string = \"{\\n \\\"cli_contracts\\\": \\\"0.1.0\\\",\\n \\\"info\\\": {\\n \\\"title\\\": \\\"AaaC Observability CLI\\\",\\n \\\"version\\\": \\\"0.1.0\\\",\\n \\\"description\\\": \\\"aaac-observ — external event registration and query for @aaac/observability\\\"\\n },\\n \\\"command_sets\\\": {\\n \\\"aaac-observ\\\": {\\n \\\"summary\\\": \\\"aaac-observ — register external events and query the observability store\\\",\\n \\\"executable\\\": \\\"aaac-observ\\\",\\n \\\"commands\\\": {\\n \\\"record\\\": {\\n \\\"summary\\\": \\\"Register an external event into the observability pipeline (thin entrypoint for EventCollector.registerExternalEvent)\\\",\\n \\\"options\\\": [\\n {\\n \\\"name\\\": \\\"event-type\\\",\\n \\\"aliases\\\": [\\n \\\"t\\\"\\n ],\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Event type (e.g. promotion.commit, process.edit, agent.session)\\\"\\n },\\n {\\n \\\"name\\\": \\\"lifecycle\\\",\\n \\\"aliases\\\": [\\n \\\"l\\\"\\n ],\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\",\\n \\\"enum\\\": [\\n \\\"open\\\",\\n \\\"close\\\",\\n \\\"event\\\",\\n \\\"instant\\\"\\n ]\\n },\\n \\\"description\\\": \\\"Event lifecycle phase: open | close | event | instant\\\"\\n },\\n {\\n \\\"name\\\": \\\"span-id\\\",\\n \\\"aliases\\\": [\\n \\\"s\\\"\\n ],\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Span ID (auto-generated if omitted)\\\"\\n },\\n {\\n \\\"name\\\": \\\"parent-span-id\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Parent span ID for hierarchy\\\"\\n },\\n {\\n \\\"name\\\": \\\"session-id\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Session ID to associate with the event\\\"\\n },\\n {\\n \\\"name\\\": \\\"trace-id\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Trace ID for distributed tracing\\\"\\n },\\n {\\n \\\"name\\\": \\\"source\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Event source identifier (default: external)\\\"\\n },\\n {\\n \\\"name\\\": \\\"attr\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"array\\\",\\n \\\"items\\\": {\\n \\\"type\\\": \\\"string\\\"\\n }\\n },\\n \\\"description\\\": \\\"Attribute as key=value (repeatable, e.g. --attr git.commit=abc123)\\\"\\n },\\n {\\n \\\"name\\\": \\\"link\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"array\\\",\\n \\\"items\\\": {\\n \\\"type\\\": \\\"string\\\"\\n }\\n },\\n \\\"description\\\": \\\"Cross-axis link as targetSpanId:linkType[:targetTraceId] (repeatable)\\\"\\n },\\n {\\n \\\"name\\\": \\\"db\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Path to observability SQLite database (overrides default)\\\"\\n }\\n ],\\n \\\"exits\\\": {\\n \\\"0\\\": {\\n \\\"description\\\": \\\"Event registered successfully\\\",\\n \\\"stdout\\\": {\\n \\\"format\\\": \\\"json\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"object\\\",\\n \\\"properties\\\": {\\n \\\"eventId\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"spanId\\\": {\\n \\\"type\\\": \\\"string\\\"\\n }\\n }\\n }\\n }\\n },\\n \\\"1\\\": {\\n \\\"description\\\": \\\"Registration failed (validation error or write error)\\\",\\n \\\"stderr\\\": {\\n \\\"format\\\": \\\"text\\\"\\n }\\n }\\n }\\n },\\n \\\"query\\\": {\\n \\\"summary\\\": \\\"Query the observability store via QueryAdapter\\\",\\n \\\"options\\\": [\\n {\\n \\\"name\\\": \\\"kind\\\",\\n \\\"aliases\\\": [\\n \\\"k\\\"\\n ],\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\",\\n \\\"enum\\\": [\\n \\\"trace\\\",\\n \\\"span\\\",\\n \\\"search\\\",\\n \\\"links\\\"\\n ]\\n },\\n \\\"description\\\": \\\"Query kind: trace | span | search | links\\\"\\n },\\n {\\n \\\"name\\\": \\\"trace-id\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Trace ID for trace queries (--kind trace)\\\"\\n },\\n {\\n \\\"name\\\": \\\"span-id\\\",\\n \\\"aliases\\\": [\\n \\\"s\\\"\\n ],\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Span ID for span/links queries (--kind span or --kind links)\\\"\\n },\\n {\\n \\\"name\\\": \\\"event-type\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Event type filter for search queries (--kind search)\\\"\\n },\\n {\\n \\\"name\\\": \\\"task-id\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Task ID filter for search queries (--kind search)\\\"\\n },\\n {\\n \\\"name\\\": \\\"from\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Start time in ISO 8601 format for search queries (--kind search)\\\"\\n },\\n {\\n \\\"name\\\": \\\"to\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"End time in ISO 8601 format for search queries (--kind search)\\\"\\n },\\n {\\n \\\"name\\\": \\\"direction\\\",\\n \\\"aliases\\\": [\\n \\\"d\\\"\\n ],\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\",\\n \\\"enum\\\": [\\n \\\"forward\\\",\\n \\\"reverse\\\",\\n \\\"both\\\"\\n ]\\n },\\n \\\"description\\\": \\\"Link traversal direction for links queries (default: both)\\\"\\n },\\n {\\n \\\"name\\\": \\\"db\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Path to observability SQLite database (overrides default)\\\"\\n }\\n ],\\n \\\"exits\\\": {\\n \\\"0\\\": {\\n \\\"description\\\": \\\"Query succeeded — results as JSON array\\\",\\n \\\"stdout\\\": {\\n \\\"format\\\": \\\"json\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"object\\\"\\n }\\n }\\n },\\n \\\"1\\\": {\\n \\\"description\\\": \\\"Query failed\\\",\\n \\\"stderr\\\": {\\n \\\"format\\\": \\\"text\\\"\\n }\\n }\\n }\\n }\\n }\\n }\\n }\\n}\";\n","/**\n * record handler — thin entrypoint for EventCollector.registerExternalEvent.\n *\n * Wires the CLI options to the write pipeline:\n * options → EventCollector.registerExternalEvent\n * → Normalizer → Correlator → Enricher → SqliteSink + OtelEmitter\n *\n * architecture.md §9 / OBSERVABILITY_TASKS.md Phase 3-2\n * shift-left-event-mapping.md: aaac-observ record is the thin entrypoint\n */\nimport { createPipeline, generateId, DEFAULT_DB_PATH } from \"../index.js\";\nimport type { CanonicalLink, AttrValue, Lifecycle } from \"../types/canonical-event.js\";\n\n// ── Parsers ──────────────────────────────────────────────────────────────────\n\n/**\n * Parse comma-separated key=value attribute string into a Record.\n * Also accepts a JSON object string.\n *\n * Examples:\n * \"git.commit=abc123,git.branch=main\"\n * '{\"git.commit\":\"abc123\",\"git.branch\":\"main\"}'\n */\nexport function parseAttr(attrStr: string | undefined): Record<string, AttrValue> {\n if (!attrStr) return {};\n const trimmed = attrStr.trim();\n if (trimmed.startsWith(\"{\")) {\n try {\n return JSON.parse(trimmed) as Record<string, AttrValue>;\n } catch {\n // fall through to key=value parsing\n }\n }\n const result: Record<string, AttrValue> = {};\n for (const pair of trimmed.split(\",\")) {\n const eqIdx = pair.indexOf(\"=\");\n if (eqIdx > 0) {\n const key = pair.slice(0, eqIdx).trim();\n const val = pair.slice(eqIdx + 1).trim();\n result[key] = val;\n }\n }\n return result;\n}\n\n/**\n * Parse comma-separated link string into a CanonicalLink array.\n * Format per item: targetSpanId:linkType[:targetTraceId]\n *\n * Example: \"spanId123:materializes_as_commit,spanId456:contains_change:traceId789\"\n */\nexport function parseLinks(linkStr: string | undefined): CanonicalLink[] {\n if (!linkStr) return [];\n const links: CanonicalLink[] = [];\n for (const item of linkStr.split(\",\")) {\n const parts = item.trim().split(\":\");\n if (parts.length >= 2) {\n const targetSpanId = parts[0].trim();\n const linkType = parts[1].trim();\n const targetTraceId = parts[2]?.trim() || undefined;\n if (targetSpanId && linkType) {\n links.push({ targetSpanId, linkType, targetTraceId });\n }\n }\n }\n return links;\n}\n\n// ── Handler ──────────────────────────────────────────────────────────────────\n\n/**\n * Options as parsed from the generated CommandHandlers interface.\n * NOTE: attr and link are strings (last value from --option) not arrays,\n * because the generated Commander program does not use a collector.\n * Comma-separate multiple values: --attr \"key1=val1,key2=val2\"\n */\nexport interface RecordHandlerOptions {\n eventType?: string;\n lifecycle?: string;\n spanId?: string;\n parentSpanId?: string;\n sessionId?: string;\n traceId?: string;\n source?: string;\n attr?: string;\n link?: string;\n db?: string;\n}\n\n/**\n * Handle `aaac-observ record`.\n *\n * Required options: --event-type, --lifecycle\n * On success: writes the event through the full pipeline and prints\n * { eventId, spanId } to stdout.\n * On error: writes to stderr and exits with code 1.\n */\nexport async function handleRecord(\n options: RecordHandlerOptions,\n _parentOpts: Record<string, unknown>,\n): Promise<void> {\n // ── Validate required options ──────────────────────────────────────────────\n if (!options.eventType) {\n process.stderr.write(\"Error: --event-type (-t) is required\\n\");\n process.exit(1);\n }\n const validLifecycles: readonly string[] = [\"open\", \"close\", \"event\", \"instant\"];\n if (!options.lifecycle) {\n process.stderr.write(\n `Error: --lifecycle (-l) is required (${validLifecycles.join(\"|\")})\\n`,\n );\n process.exit(1);\n }\n if (!validLifecycles.includes(options.lifecycle)) {\n process.stderr.write(\n `Error: --lifecycle must be one of: ${validLifecycles.join(\"|\")}\\n`,\n );\n process.exit(1);\n }\n\n // ── Build event payload ────────────────────────────────────────────────────\n const dbPath = options.db ?? DEFAULT_DB_PATH;\n const spanId = options.spanId ?? generateId();\n const attributes = parseAttr(options.attr);\n const links = parseLinks(options.link);\n\n // Inject well-known scalars into attributes so the Normalizer picks them up\n if (options.sessionId) {\n attributes[\"session_id\"] = options.sessionId;\n }\n // trace_id stored as attribute for user reference; canonical traceId is\n // managed by Normalizer/Correlator (propagated via parentSpanId chain)\n if (options.traceId) {\n attributes[\"trace_id\"] = options.traceId;\n }\n\n // ── Build & execute pipeline ───────────────────────────────────────────────\n let capturedEventId: string | undefined;\n\n const { collector, sink } = createPipeline({\n dbPath,\n // Use afterCorrelate to capture the event id before it is written\n afterCorrelate: (event) => {\n capturedEventId = event.id;\n return event;\n },\n // Disable enrichment rules for external CLI events to keep the write\n // path lightweight (no Enricher cache, no artifact lookups, no derived events)\n enrichRules: [],\n });\n\n try {\n collector.registerExternalEvent({\n source: options.source ?? \"external\",\n eventType: options.eventType,\n lifecycle: options.lifecycle as Lifecycle,\n spanId,\n parentSpanId: options.parentSpanId,\n attributes,\n links,\n });\n } catch (err) {\n process.stderr.write(\n `Error: ${err instanceof Error ? err.message : String(err)}\\n`,\n );\n sink.close();\n process.exit(1);\n }\n\n sink.close();\n\n const result = { eventId: capturedEventId, spanId };\n process.stdout.write(JSON.stringify(result) + \"\\n\");\n}\n","/**\n * query handler — read path via QueryAdapter.\n *\n * Routes --kind to the appropriate SqliteQueryAdapter method and emits\n * results as JSON to stdout.\n *\n * Supported kinds:\n * trace → QueryAdapter.getTrace(--trace-id)\n * span → QueryAdapter.getSpan(--span-id) [+ QueryAdapter.getSpanEvents]\n * search → QueryAdapter.querySpans({ eventType, taskId, from, to })\n * links → QueryAdapter.getLinks(--span-id, --direction)\n *\n * architecture.md §12 / OBSERVABILITY_TASKS.md Phase 3-2 (record CLI + query)\n */\nimport { SqliteQueryAdapter, DEFAULT_DB_PATH } from \"../index.js\";\nimport { isoToUnixNano } from \"../types/ids.js\";\nimport type { LinkDirection } from \"../query/models.js\";\n\n// ── Handler ───────────────────────────────────────────────────────────────────\n\n/**\n * Options as parsed from the generated CommandHandlers interface.\n */\nexport interface QueryHandlerOptions {\n kind?: string;\n traceId?: string;\n spanId?: string;\n eventType?: string;\n taskId?: string;\n from?: string;\n to?: string;\n direction?: string;\n db?: string;\n}\n\n/** JSON replacer that serialises BigInt as a string. */\nfunction bigIntReplacer(_key: string, value: unknown): unknown {\n return typeof value === \"bigint\" ? value.toString() : value;\n}\n\n/**\n * Handle `aaac-observ query`.\n *\n * Required option: --kind (trace|span|search|links)\n * On success: writes a JSON array/object to stdout.\n * On error: writes to stderr and exits with code 1.\n *\n * DESIGN NOTE: all required-arg validation happens BEFORE the adapter is\n * created, so that when process.exit is mocked to throw in tests there is no\n * risk of a double-close on the database connection.\n */\nexport async function handleQuery(\n options: QueryHandlerOptions,\n _parentOpts: Record<string, unknown>,\n): Promise<void> {\n const validKinds = [\"trace\", \"span\", \"search\", \"links\"] as const;\n type QueryKind = (typeof validKinds)[number];\n\n // ── Validate options (before opening the adapter) ─────────────────────────\n if (!options.kind) {\n process.stderr.write(\n `Error: --kind (-k) is required (${validKinds.join(\"|\")})\\n`,\n );\n process.exit(1);\n }\n if (!validKinds.includes(options.kind as QueryKind)) {\n process.stderr.write(\n `Error: --kind must be one of: ${validKinds.join(\"|\")}\\n`,\n );\n process.exit(1);\n }\n\n const kind = options.kind as QueryKind;\n\n // Validate sub-command required args (before adapter creation)\n if (kind === \"trace\" && !options.traceId) {\n process.stderr.write(\"Error: --trace-id is required for --kind trace\\n\");\n process.exit(1);\n }\n if (kind === \"span\" && !options.spanId) {\n process.stderr.write(\"Error: --span-id is required for --kind span\\n\");\n process.exit(1);\n }\n if (kind === \"links\" && !options.spanId) {\n process.stderr.write(\"Error: --span-id is required for --kind links\\n\");\n process.exit(1);\n }\n\n // ── Open adapter ──────────────────────────────────────────────────────────\n const dbPath = options.db ?? DEFAULT_DB_PATH;\n const adapter = new SqliteQueryAdapter(dbPath);\n\n try {\n let result: unknown;\n\n switch (kind) {\n // ── trace: all spans in a trace ─────────────────────────────────────\n case \"trace\": {\n // options.traceId is guaranteed by pre-validation above\n result = adapter.getTrace(options.traceId!);\n break;\n }\n\n // ── span: single span + its events ──────────────────────────────────\n case \"span\": {\n // options.spanId is guaranteed by pre-validation above\n const span = adapter.getSpan(options.spanId!);\n const events = adapter.getSpanEvents(options.spanId!);\n result = span !== undefined ? { span, events } : null;\n break;\n }\n\n // ── search: spans matching event-type / task-id / time range ─────────\n case \"search\": {\n const filter: Parameters<typeof adapter.querySpans>[0] = {};\n if (options.eventType) filter.eventType = options.eventType;\n if (options.taskId) filter.taskId = options.taskId;\n if (options.from) filter.fromTimeUnixNano = isoToUnixNano(options.from);\n if (options.to) filter.toTimeUnixNano = isoToUnixNano(options.to);\n result = adapter.querySpans(filter);\n break;\n }\n\n // ── links: link traversal ─────────────────────────────────────────────\n case \"links\": {\n const validDirections = [\"forward\", \"reverse\", \"both\"] as const;\n type Dir = (typeof validDirections)[number];\n const dir: Dir =\n options.direction !== undefined &&\n validDirections.includes(options.direction as Dir)\n ? (options.direction as Dir)\n : \"both\";\n result = adapter.getLinks(options.spanId!, dir as LinkDirection);\n break;\n }\n }\n\n process.stdout.write(JSON.stringify(result, bigIntReplacer) + \"\\n\");\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n\n // Graceful handling: an un-initialised (fresh empty) database has no\n // tables yet. Return an empty / null result rather than an error so that\n // `aaac-observ query` on a machine with no recorded events behaves\n // predictably.\n if (msg.includes(\"no such table\") || msg.includes(\"no such column\")) {\n const emptyResult = kind === \"span\" ? null : [];\n process.stdout.write(JSON.stringify(emptyResult, bigIntReplacer) + \"\\n\");\n adapter.close();\n return;\n }\n\n process.stderr.write(`Error: ${msg}\\n`);\n adapter.close();\n process.exit(1);\n }\n\n adapter.close();\n}\n","/**\n * CLI command handlers for aaac-observ.\n *\n * Implements the CommandHandlers interface generated by cli-contracts and\n * delegates to the domain-specific handler modules.\n *\n * Usage (in index.ts):\n * import { handlers } from './handlers.js';\n * import { createProgram } from '../generated/program.js';\n * const program = createProgram(handlers, version);\n * await program.parseAsync();\n */\nimport type { CommandHandlers } from \"../generated/program.js\";\nimport { handleRecord } from \"./record.js\";\nimport { handleQuery } from \"./query.js\";\n\nexport const handlers: CommandHandlers = {\n record: handleRecord,\n query: handleQuery,\n};\n"],"mappings":";;;;;;;;;;AAOA,SAAS,oBAAoB;AAC7B,SAAS,SAAS,eAAe;AACjC,SAAS,qBAAqB;;;ACR9B,SAAS,eAAe;;;ACEjB,IAAM,gBAAwB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAE9B,IAAM,oBAA4B;;;ADIlC,SAAS,cACdA,WACA,SACS;AACT,QAAMC,WAAU,IAAI,QAAQ;AAC5B,EAAAA,SACG,KAAK,aAAa,EAClB,QAAQ,SAAS,eAAe,EAChC,YAAY,+EAA0E;AAGzF,EAAAA,SACG,QAAQ,QAAQ,EAChB,YAAY,uHAAuH,EACnI,OAAO,4BAA4B,iEAAiE,EACpG,OAAO,2BAA2B,uDAAuD,EACzF,OAAO,yBAAyB,qCAAqC,EACrE,OAAO,4BAA4B,8BAA8B,EACjE,OAAO,wBAAwB,wCAAwC,EACvE,OAAO,sBAAsB,kCAAkC,EAC/D,OAAO,oBAAoB,6CAA6C,EACxE,OAAO,kBAAkB,oEAAoE,EAC7F,OAAO,kBAAkB,uEAAuE,EAChG,OAAO,gBAAgB,2DAA2D,EAClF,OAAO,OAAO,MAAM,QAAQ;AAC3B,UAAMD,UAAS,OAAO,MAAM,IAAI,gBAAgB,CAAC;AAAA,EACnD,CAAC;AAEH,EAAAC,SACG,QAAQ,OAAO,EACf,YAAY,gDAAgD,EAC5D,OAAO,sBAAsB,2CAA2C,EACxE,OAAO,sBAAsB,2CAA2C,EACxE,OAAO,yBAAyB,8DAA8D,EAC9F,OAAO,wBAAwB,sDAAsD,EACrF,OAAO,qBAAqB,mDAAmD,EAC/E,OAAO,kBAAkB,kEAAkE,EAC3F,OAAO,gBAAgB,gEAAgE,EACvF,OAAO,2BAA2B,4DAA4D,EAC9F,OAAO,gBAAgB,2DAA2D,EAClF,OAAO,OAAO,MAAM,QAAQ;AAC3B,UAAMD,UAAS,MAAM,MAAM,IAAI,gBAAgB,CAAC;AAAA,EAClD,CAAC;AAIH,EAAAC,SACG,QAAQ,SAAS,EACjB,YAAY,mDAAmD,EAC/D,SAAS,iBAAiB,2CAA2C,EACrE,OAAO,aAAa,yBAAyB,KAAK,EAClD,OAAO,kBAAkB,gCAAgC,IAAI,EAC7D,OAAO,yBAAyB,iCAAiC,MAAM,EACvE,OAAO,OAAO,UAAoB,MAAiE,QAAiB;AACnH,QAAI,SAAS,WAAW,KAAK,CAAC,KAAK,KAAK;AACtC,cAAQ,OAAO,MAAM,KAAK,UAAU,EAAE,MAAM,gBAAgB,SAAS,mCAAmC,CAAC,IAAI,IAAI;AACjH,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,UAAM,SAAS,KAAK,UAAU;AAC9B,UAAM,MAAM,KAAK,MAAM,iBAAiB;AACxC,UAAM,SAAS,KAAK,MAAM,CAAC,IAAI;AAE/B,QAAI,OAAO,WAAW,GAAG;AAEvB,UAAI,WAAW,QAAQ;AACrB,cAAM,MAA+B,CAAC;AACtC,YAAI,KAAK,aAAa;AACpB,cAAI,QAAQ;AAAA,YACV,QAAQ;AAAA,YACR,MAAM;AAAA,YACN,cAAa,oBAAI,KAAK,GAAE,YAAY;AAAA,YACpC,aAAa,IAAI,iBAAiB;AAAA,YAClC,UAAU,CAAC,sBAAqB,mBAAmB;AAAA,UACrD;AAAA,QACF;AACA,eAAO,OAAO,KAAK,GAAG;AACtB,gBAAQ,OAAO,MAAM,KAAK,UAAU,KAAK,MAAM,CAAC,IAAI,IAAI;AAAA,MAC1D,OAAO;AAEL,cAAM,YAAsB,CAAC;AAC7B,kBAAU,KAAK,uBAAuB;AACtC,kBAAU,KAAK,oBAAoB;AACnC,kBAAU,KAAK,uCAAuC;AACtD,YAAI,KAAK,aAAa;AACpB,oBAAU,KAAK,KAAK;AACpB,oBAAU,KAAK,kBAAkB;AACjC,oBAAU,KAAK,qCAAqC;AACpD,oBAAU,KAAK,mBAAkB,oBAAI,KAAK,GAAE,YAAY,CAAC;AACzD,oBAAU,KAAK,oBAAoB,IAAI,iBAAiB,QAAQ;AAChE,oBAAU,KAAK,WAAW;AAC1B,qBAAW,MAAM,CAAC,sBAAqB,mBAAmB,GAAG;AAC3D,sBAAU,KAAK,SAAS,EAAE;AAAA,UAC5B;AAAA,QACF;AACA,kBAAU,KAAK,KAAK;AACpB,kBAAU,KAAK,aAAa;AAC5B,gBAAQ,OAAO,MAAM,UAAU,KAAK,IAAI,IAAI,IAAI;AAAA,MAClD;AAAA,IACF,OAAO;AAEL,YAAM,WAAoC;AAAA,QACxC,eAAe,IAAI;AAAA,QACnB,MAAM,IAAI;AAAA,QACV,cAAc,CAAC;AAAA,MACjB;AACA,YAAM,MAAM,SAAS;AACrB,iBAAW,CAAC,OAAO,EAAE,KAAK,OAAO,QAAQ,IAAI,gBAAgB,CAAC,CAAC,GAAG;AAChE,cAAM,OAAQ,GAA+B;AAC7C,YAAI,CAAC,KAAM;AACX,cAAM,UAAmC,CAAC;AAC1C,mBAAW,CAAC,OAAO,MAAM,KAAK,OAAO,QAAQ,IAAI,GAAG;AAClD,gBAAM,SAAS,QAAQ,MAAM;AAC7B,cAAI,OAAO,KAAK,CAAC,OAAO,OAAO,SAAS,OAAO,UAAU,MAAM,WAAW,KAAK,GAAG,CAAC,GAAG;AACpF,oBAAQ,KAAK,IAAI;AAAA,UACnB;AAAA,QACF;AACA,YAAI,OAAO,KAAK,OAAO,EAAE,SAAS,GAAG;AACnC,gBAAM,UAAU,EAAE,GAAI,GAA+B;AACrD,kBAAQ,WAAW;AACnB,cAAI,KAAK,IAAI;AAAA,QACf;AAAA,MACF;AACA,UAAI,IAAI,WAAY,UAAS,aAAa,IAAI;AAC9C,cAAQ,OAAO,MAAM,KAAK,UAAU,UAAU,MAAM,CAAC,IAAI,IAAI;AAAA,IAC/D;AACA,YAAQ,KAAK,CAAC;AAAA,EAChB,CAAC;AACH,SAAOA;AACT;;;AEnHO,SAAS,UAAU,SAAwD;AAChF,MAAI,CAAC,QAAS,QAAO,CAAC;AACtB,QAAM,UAAU,QAAQ,KAAK;AAC7B,MAAI,QAAQ,WAAW,GAAG,GAAG;AAC3B,QAAI;AACF,aAAO,KAAK,MAAM,OAAO;AAAA,IAC3B,QAAQ;AAAA,IAER;AAAA,EACF;AACA,QAAM,SAAoC,CAAC;AAC3C,aAAW,QAAQ,QAAQ,MAAM,GAAG,GAAG;AACrC,UAAM,QAAQ,KAAK,QAAQ,GAAG;AAC9B,QAAI,QAAQ,GAAG;AACb,YAAM,MAAM,KAAK,MAAM,GAAG,KAAK,EAAE,KAAK;AACtC,YAAM,MAAM,KAAK,MAAM,QAAQ,CAAC,EAAE,KAAK;AACvC,aAAO,GAAG,IAAI;AAAA,IAChB;AAAA,EACF;AACA,SAAO;AACT;AAQO,SAAS,WAAW,SAA8C;AACvE,MAAI,CAAC,QAAS,QAAO,CAAC;AACtB,QAAM,QAAyB,CAAC;AAChC,aAAW,QAAQ,QAAQ,MAAM,GAAG,GAAG;AACrC,UAAM,QAAQ,KAAK,KAAK,EAAE,MAAM,GAAG;AACnC,QAAI,MAAM,UAAU,GAAG;AACrB,YAAM,eAAe,MAAM,CAAC,EAAE,KAAK;AACnC,YAAM,WAAW,MAAM,CAAC,EAAE,KAAK;AAC/B,YAAM,gBAAgB,MAAM,CAAC,GAAG,KAAK,KAAK;AAC1C,UAAI,gBAAgB,UAAU;AAC5B,cAAM,KAAK,EAAE,cAAc,UAAU,cAAc,CAAC;AAAA,MACtD;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AA+BA,eAAsB,aACpB,SACA,aACe;AAEf,MAAI,CAAC,QAAQ,WAAW;AACtB,YAAQ,OAAO,MAAM,wCAAwC;AAC7D,YAAQ,KAAK,CAAC;AAAA,EAChB;AACA,QAAM,kBAAqC,CAAC,QAAQ,SAAS,SAAS,SAAS;AAC/E,MAAI,CAAC,QAAQ,WAAW;AACtB,YAAQ,OAAO;AAAA,MACb,wCAAwC,gBAAgB,KAAK,GAAG,CAAC;AAAA;AAAA,IACnE;AACA,YAAQ,KAAK,CAAC;AAAA,EAChB;AACA,MAAI,CAAC,gBAAgB,SAAS,QAAQ,SAAS,GAAG;AAChD,YAAQ,OAAO;AAAA,MACb,sCAAsC,gBAAgB,KAAK,GAAG,CAAC;AAAA;AAAA,IACjE;AACA,YAAQ,KAAK,CAAC;AAAA,EAChB;AAGA,QAAM,SAAS,QAAQ,MAAM;AAC7B,QAAM,SAAS,QAAQ,UAAU,WAAW;AAC5C,QAAM,aAAa,UAAU,QAAQ,IAAI;AACzC,QAAM,QAAQ,WAAW,QAAQ,IAAI;AAGrC,MAAI,QAAQ,WAAW;AACrB,eAAW,YAAY,IAAI,QAAQ;AAAA,EACrC;AAGA,MAAI,QAAQ,SAAS;AACnB,eAAW,UAAU,IAAI,QAAQ;AAAA,EACnC;AAGA,MAAI;AAEJ,QAAM,EAAE,WAAW,KAAK,IAAI,eAAe;AAAA,IACzC;AAAA;AAAA,IAEA,gBAAgB,CAAC,UAAU;AACzB,wBAAkB,MAAM;AACxB,aAAO;AAAA,IACT;AAAA;AAAA;AAAA,IAGA,aAAa,CAAC;AAAA,EAChB,CAAC;AAED,MAAI;AACF,cAAU,sBAAsB;AAAA,MAC9B,QAAQ,QAAQ,UAAU;AAAA,MAC1B,WAAW,QAAQ;AAAA,MACnB,WAAW,QAAQ;AAAA,MACnB;AAAA,MACA,cAAc,QAAQ;AAAA,MACtB;AAAA,MACA;AAAA,IACF,CAAC;AAAA,EACH,SAAS,KAAK;AACZ,YAAQ,OAAO;AAAA,MACb,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA;AAAA,IAC5D;AACA,SAAK,MAAM;AACX,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,OAAK,MAAM;AAEX,QAAM,SAAS,EAAE,SAAS,iBAAiB,OAAO;AAClD,UAAQ,OAAO,MAAM,KAAK,UAAU,MAAM,IAAI,IAAI;AACpD;;;ACzIA,SAAS,eAAe,MAAc,OAAyB;AAC7D,SAAO,OAAO,UAAU,WAAW,MAAM,SAAS,IAAI;AACxD;AAaA,eAAsB,YACpB,SACA,aACe;AACf,QAAM,aAAa,CAAC,SAAS,QAAQ,UAAU,OAAO;AAItD,MAAI,CAAC,QAAQ,MAAM;AACjB,YAAQ,OAAO;AAAA,MACb,mCAAmC,WAAW,KAAK,GAAG,CAAC;AAAA;AAAA,IACzD;AACA,YAAQ,KAAK,CAAC;AAAA,EAChB;AACA,MAAI,CAAC,WAAW,SAAS,QAAQ,IAAiB,GAAG;AACnD,YAAQ,OAAO;AAAA,MACb,iCAAiC,WAAW,KAAK,GAAG,CAAC;AAAA;AAAA,IACvD;AACA,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,OAAO,QAAQ;AAGrB,MAAI,SAAS,WAAW,CAAC,QAAQ,SAAS;AACxC,YAAQ,OAAO,MAAM,kDAAkD;AACvE,YAAQ,KAAK,CAAC;AAAA,EAChB;AACA,MAAI,SAAS,UAAU,CAAC,QAAQ,QAAQ;AACtC,YAAQ,OAAO,MAAM,gDAAgD;AACrE,YAAQ,KAAK,CAAC;AAAA,EAChB;AACA,MAAI,SAAS,WAAW,CAAC,QAAQ,QAAQ;AACvC,YAAQ,OAAO,MAAM,iDAAiD;AACtE,YAAQ,KAAK,CAAC;AAAA,EAChB;AAGA,QAAM,SAAS,QAAQ,MAAM;AAC7B,QAAM,UAAU,IAAI,mBAAmB,MAAM;AAE7C,MAAI;AACF,QAAI;AAEJ,YAAQ,MAAM;AAAA;AAAA,MAEZ,KAAK,SAAS;AAEZ,iBAAS,QAAQ,SAAS,QAAQ,OAAQ;AAC1C;AAAA,MACF;AAAA;AAAA,MAGA,KAAK,QAAQ;AAEX,cAAM,OAAO,QAAQ,QAAQ,QAAQ,MAAO;AAC5C,cAAM,SAAS,QAAQ,cAAc,QAAQ,MAAO;AACpD,iBAAS,SAAS,SAAY,EAAE,MAAM,OAAO,IAAI;AACjD;AAAA,MACF;AAAA;AAAA,MAGA,KAAK,UAAU;AACb,cAAM,SAAmD,CAAC;AAC1D,YAAI,QAAQ,UAAW,QAAO,YAAY,QAAQ;AAClD,YAAI,QAAQ,OAAQ,QAAO,SAAS,QAAQ;AAC5C,YAAI,QAAQ,KAAM,QAAO,mBAAmB,cAAc,QAAQ,IAAI;AACtE,YAAI,QAAQ,GAAI,QAAO,iBAAiB,cAAc,QAAQ,EAAE;AAChE,iBAAS,QAAQ,WAAW,MAAM;AAClC;AAAA,MACF;AAAA;AAAA,MAGA,KAAK,SAAS;AACZ,cAAM,kBAAkB,CAAC,WAAW,WAAW,MAAM;AAErD,cAAM,MACJ,QAAQ,cAAc,UACtB,gBAAgB,SAAS,QAAQ,SAAgB,IAC5C,QAAQ,YACT;AACN,iBAAS,QAAQ,SAAS,QAAQ,QAAS,GAAoB;AAC/D;AAAA,MACF;AAAA,IACF;AAEA,YAAQ,OAAO,MAAM,KAAK,UAAU,QAAQ,cAAc,IAAI,IAAI;AAAA,EACpE,SAAS,KAAK;AACZ,UAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAM3D,QAAI,IAAI,SAAS,eAAe,KAAK,IAAI,SAAS,gBAAgB,GAAG;AACnE,YAAM,cAAc,SAAS,SAAS,OAAO,CAAC;AAC9C,cAAQ,OAAO,MAAM,KAAK,UAAU,aAAa,cAAc,IAAI,IAAI;AACvE,cAAQ,MAAM;AACd;AAAA,IACF;AAEA,YAAQ,OAAO,MAAM,UAAU,GAAG;AAAA,CAAI;AACtC,YAAQ,MAAM;AACd,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,UAAQ,MAAM;AAChB;;;AC9IO,IAAM,WAA4B;AAAA,EACvC,QAAQ;AAAA,EACR,OAAO;AACT;;;ALNA,IAAM,YAAY,QAAQ,cAAc,YAAY,GAAG,CAAC;AACxD,IAAM,MAAM,KAAK;AAAA,EACf,aAAa,QAAQ,WAAW,oBAAoB,GAAG,MAAM;AAC/D;AAEA,IAAM,UAAU,cAAc,UAAU,IAAI,OAAO;AACnD,MAAM,QAAQ,WAAW;","names":["handlers","program"]}
|