@dx-do/cli 6.0.1 → 6.0.4

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 (43) hide show
  1. package/README.md +12 -6
  2. package/dist-node/{03-discover-sources.nassql.data-store-by6sqk23.json5 → 03-discover-sources.nassql.data-store-d6hb6wf7.json5} +4 -3
  3. package/dist-node/{20-nassql-from-metadata-basic.nassql.data-store-zdf1gp1v.json5 → 20-nassql-from-metadata-basic.nassql.data-store-mq1hn0qz.json5} +4 -2
  4. package/dist-node/34-nassql-entity-to-metric-ids.nassql-jn6t8zxs.md +76 -0
  5. package/dist-node/34-nassql-entity-to-metric-ids.nassql.data-store-t8ecm336.json5 +69 -0
  6. package/dist-node/34-nassql-entity-to-metric-ids.nassql.data-store-tmd-9vbg6p76.json +4 -0
  7. package/dist-node/40-metadata-filter-by-typeenum.metadata-kmcc85ac.md +66 -0
  8. package/dist-node/40-metadata-filter-by-typeenum.metadata.data-store-b4erpx6h.json5 +52 -0
  9. package/dist-node/40-metadata-filter-by-typeenum.metadata.data-store-tmd-3zg3n0j1.json +4 -0
  10. package/dist-node/41-metadata-frontend-durations.metadata-n2ba62j7.md +50 -0
  11. package/dist-node/41-metadata-frontend-durations.metadata.data-store-kpkhaf6c.json5 +48 -0
  12. package/dist-node/41-metadata-frontend-durations.metadata.data-store-tmd-c685fc9v.json +4 -0
  13. package/dist-node/42-metadata-metric-id-to-entities.metadata-qg0p7y71.md +67 -0
  14. package/dist-node/42-metadata-metric-id-to-entities.metadata.data-store-tmd-zd99v6pz.json +4 -0
  15. package/dist-node/42-metadata-metric-id-to-entities.metadata.data-store-vwv8dq0e.json5 +52 -0
  16. package/dist-node/{SKILL-1xn7r9nt.md → SKILL-86r2cm61.md} +26 -2
  17. package/dist-node/{agentic-mcp-rycd2gh8.md → agentic-mcp-9nz7zh8d.md} +2 -1
  18. package/dist-node/alarms-cookbook-xrwbrdte.md +160 -0
  19. package/dist-node/alarms-quickstart-fhg59mr2.md +100 -0
  20. package/dist-node/{chunk-RNMHSXZF-pdwasrg7.js → chunk-4L5DIG2E-9p3rrkp9.js} +1 -1
  21. package/dist-node/{chunk-VV2FJEMA-3rvtkmga.js → chunk-4Q347M53-e6wyjnpc.js} +2 -2
  22. package/dist-node/{chunk-Q2JA73UH-akkb8bh3.js → chunk-6TL6OWJA-jrr526kw.js} +1 -1
  23. package/dist-node/chunk-7G4RSOZU-nnmp25yx.js +3753 -0
  24. package/dist-node/{chunk-YVD3UK5I-9pxr1jka.js → chunk-JYKZ5EMU-6jq5egbw.js} +3 -3
  25. package/dist-node/{chunk-5VSFINOX-ewzpx7wh.js → chunk-OV4PNSIK-pqs8y3nm.js} +3 -3
  26. package/dist-node/{chunk-JRM4BLOM-rg32z8w4.js → chunk-P6TRLBVU-rxz4tacs.js} +1 -1
  27. package/dist-node/{chunk-4I3HBO6U-2ebgf7kh.js → chunk-TFFMCZCS-rhchxvk7.js} +1 -1
  28. package/dist-node/{chunk-4PMCLJMS-0mqvr4m4.js → chunk-W5FHICA2-x50sz7wk.js} +1 -1
  29. package/dist-node/{discovery-flow-fw79kbx4.md → discovery-flow-m0zp07e3.md} +13 -0
  30. package/dist-node/{gotchas-8ab64kcd.md → gotchas-p696dmam.md} +131 -0
  31. package/dist-node/{index-104hyb1m.html → index-dfkxebky.html} +1 -1
  32. package/dist-node/{index-mbzg9rhc.json → index-mf7znaf7.json} +30 -0
  33. package/dist-node/{index-g3hh5wez.json → index-x07qhy6g.json} +53 -0
  34. package/dist-node/{investigator-flow-jc2s0n46.md → investigator-flow-dcyk8v51.md} +36 -2
  35. package/dist-node/main-4VTFKCWX-6x4n4v8m.js +13 -0
  36. package/dist-node/main.js +26106 -18435
  37. package/dist-node/{metrics-grounding-2h4kkbe3.md → metrics-grounding-8vhfqya9.md} +59 -4
  38. package/dist-node/{mm-cookbook-23jpw721.md → mm-cookbook-fdwbt989.md} +1 -1
  39. package/dist-node/{nassql-cookbook-n8kc0mff.md → nassql-cookbook-kp525xra.md} +41 -1
  40. package/dist-node/{nassql-quickstart-090e0yex.md → nassql-quickstart-mth22qd3.md} +3 -1
  41. package/package.json +1 -1
  42. package/dist-node/chunk-72HYG3XZ-kf7hy4vs.js +0 -3625
  43. package/dist-node/main-SGLYO5YX-ht69eb0y.js +0 -13
@@ -10,7 +10,7 @@ related: [metric-source-names, mm-cookbook, mm-quickstart, entity-relationships,
10
10
 
11
11
  `mm-quickstart` and `mm-cookbook` cover the *mechanics* of authoring metric-metadata queries. This cookbook covers the **semantics** — what a metric *means* in DXO2's entity model, how to navigate from a metric back to the producing agent (or accept that no entity exists), and how to handle the most common surprise: metrics for resources that have no topology vertex.
12
12
 
13
- ## Metric identity, four ways
13
+ ## Metric identity, five ways
14
14
 
15
15
  The same metric is referred to by different names depending on which surface you're on:
16
16
 
@@ -20,6 +20,7 @@ The same metric is referred to by different names depending on which surface you
20
20
  | MM `SourceNameSpecifier` + `AttributeNameSpecifier` | Metrics-Metadata query input |
21
21
  | `<resource>:<metric>` | User shorthand — the `metric.path` ending after the last `:` is the metric name; the prefix is the "resource" |
22
22
  | `metric.attribute` (older) | Older API field — alias for the path's leaf segment |
23
+ | `metric.attributes.external_ids` | TAS-compatible vertex externalIds carried on every metric descriptor — the entity-association field. An array because a metric can be associated with multiple vertices. Round-trips with `FROM_TOPOLOGY`'s `VERTEX` filter without reformatting. See worked examples `42-metadata-metric-id-to-entities` (metricId → externalIds) and `34-nassql-entity-to-metric-ids` (externalId → metricIds). |
23
24
 
24
25
  Concretely, a metric like `Beans|Nass Reactive Client|Store|Flush|Unregistered Queue:Concurrent Invocations` carries:
25
26
 
@@ -60,11 +61,65 @@ The metric *value* is sometimes the only place identity lives — e.g. a `queue_
60
61
 
61
62
  When this pattern repeats for the same customer / same kind of resource, **inventorize** materializes a vertex per resource so future queries can use vertex traversal. See `lexicon/inventorize`.
62
63
 
63
- ## Metric `type` is opaque (for now)
64
+ ## Metric `type` is a bitmask — what the bits mean
64
65
 
65
- Every metric carries a numeric `type` field with values like `0`, `1`, `2`, `3`. The numeric ID indicates the metric's data shape (gauge, counter, rate, etc.) and informs how it should be aggregated and displayed.
66
+ Every metric carries a numeric `type` field. It is **not** an enum value it is a 32-bit integer composed by bitwise-OR of one or more named `typeEnum` values. A `type` of `268436481` decodes to `MONITOR_INT_DURATION` combined with `TYPE_INFO_FRONTEND` (`0x401 | 0x10000000`).
66
67
 
67
- **The numeric → semantic mapping is not yet documented in the corpus.** Treat `metric.type` as opaque: when you need to aggregate or order, prefer empirical inspection of values via `run_partial_query` rather than assuming a particular type means a particular aggregation. A future grounding pass will close this gap.
68
+ ### Bit-region layout
69
+
70
+ | Region mask | Purpose |
71
+ |---|---|
72
+ | `0x0000000F` | underlying type — INT (1), LONG (2), DOUBLE (4), STRING (5) |
73
+ | `0x000000F0` | type-property bits (server-internal flags) |
74
+ | `0x000FFF00` | numeric-info bits — DURATION (0x400), RATE (0x200), COUNTER (0x100), PERCENTAGE (0x1000), INTERVAL_COUNTER (0x2000), SATURATION (0x4000) |
75
+ | `0x00F00000` | counter-info bits (server-internal counter flags) |
76
+ | `0x0F000000` | metric flags — TYPE_FUTURE (0x01000000), TYPE_VIRTUAL (0x02000000), TYPE_TAGS (0x04000000) |
77
+ | `0xF0000000` | metric-type info — TYPE_INFO_FRONTEND (0x10000000), TYPE_INFO_BACKEND (0x20000000), TYPE_INFO_BUSINESS_TRANSACTION (0x40000000) |
78
+
79
+ ### `typeEnum` value table
80
+
81
+ | typeEnum | hex | decimal |
82
+ |---|---|---|
83
+ | `MONITOR_INT_DURATION` | `0x00000401` | 1025 |
84
+ | `MONITOR_LONG_DURATION` | `0x00000402` | 1026 |
85
+ | `MONITOR_INT_RATE` | `0x00000201` | 513 |
86
+ | `MONITOR_LONG_RATE` | `0x00000202` | 514 |
87
+ | `MONITOR_PERCENTAGE` | `0x00001001` | 4097 |
88
+ | `MONITOR_INT_INTERVAL_COUNTER` | `0x00002001` | 8193 |
89
+ | `MONITOR_LONG_INTERVAL_COUNTER` | `0x00002002` | 8194 |
90
+ | `MONITOR_DOUBLE_INTERVAL_COUNTER` | `0x00002004` | 8196 |
91
+ | `MONITOR_INT_SATURATION` | `0x00004001` | 16385 |
92
+ | `MONITOR_LONG_SATURATION` | `0x00004002` | 16386 |
93
+ | `MONITOR_STRING` | `0x00000005` | 5 |
94
+ | `MONITOR_INT_COUNTER` | `0x00000101` | 257 |
95
+ | `MONITOR_LONG_COUNTER` | `0x00000102` | 258 |
96
+ | `MONITOR_DOUBLE_COUNTER` | `0x00000104` | 260 |
97
+ | `TYPE_FUTURE` | `0x01000000` | 16777216 |
98
+ | `TYPE_VIRTUAL` | `0x02000000` | 33554432 |
99
+ | `TYPE_TAGS` | `0x04000000` | 67108864 |
100
+ | `TYPE_INFO_FRONTEND` | `0x10000000` | 268435456 |
101
+ | `TYPE_INFO_BACKEND` | `0x20000000` | 536870912 |
102
+ | `TYPE_INFO_BUSINESS_TRANSACTION` | `0x40000000` | 1073741824 |
103
+
104
+ To check bit math: `composeMetricTypeBits(['MONITOR_INT_DURATION', 'TYPE_INFO_FRONTEND'])` from `@dx-do/client` returns `268436481`. The reverse is `decodeMetricTypeBits(n)`, which returns `{ matched, residual }` — a non-zero residual means the integer carries server-internal bits outside the user-facing typeEnum API (see `gotchas.md` — undocumented `MONITOR_DOUBLE_*` variants).
105
+
106
+ ### Filtering metrics by typeEnum
107
+
108
+ Use the `TYPE` AttributeNameSpecifier:
109
+
110
+ ```json
111
+ {
112
+ "op": "TYPE",
113
+ "bitMask": 1024,
114
+ "bitMatch": 1024,
115
+ "operator": "EQ",
116
+ "specifier": { "op": "ALL" }
117
+ }
118
+ ```
119
+
120
+ This catches every metric where bit 10 is set — i.e. any DURATION variant (INT, LONG, or undocumented DOUBLE). For an exact typeEnum filter set `bitMask = bitMatch = composeMetricTypeBits([...])`. The `specifier` field is required by the server even though the schema marks it optional — see `gotchas.md`.
121
+
122
+ The visual MM query editor's TYPE-op chips picker writes both fields (and the inner `specifier`) automatically when you select typeEnum names. For "any of this semantic class regardless of INT / LONG / DOUBLE", pick from the **`ANY_*` group** at the top of the dropdown — e.g. `ANY_DURATION` produces the bit-10-only mask above, `ANY_RATE` produces bit 9, etc. The `MONITOR_*` chips remain pinned to a specific underlying type; picking both `MONITOR_INT_DURATION` and `MONITOR_LONG_DURATION` is **not** the same as `ANY_DURATION` — the chips combine with AND semantics, so that pair composes to a mask no real metric satisfies. Use `ANY_DURATION` for the "INT or LONG or DOUBLE" intent.
68
123
 
69
124
  ## Pattern recognition: deciding when to inventorize
70
125
 
@@ -59,7 +59,7 @@ so each component has its own specifier sub-language with its own ops.
59
59
  | `OR` | `{ op: "OR", specifiers: [...] }` | Union |
60
60
  | `NOT` | `{ op: "NOT", specifier: ... }` | Complement |
61
61
  | `SPEC` | `{ op: "SPEC", sourceNameSpecifier?, folderNameSpecifier?, attributeNameSpecifier? }` | Structured match (the workhorse) |
62
- | `ID` | `{ op: "ID", ids: ["m-id-1", ...] }` | By metric id |
62
+ | `ID` | `{ op: "ID", ids: ["m-id-1", ...] }` | By metric id. The response's `metric.attributes.external_ids` array carries TAS-compatible vertex externalIds — the entity-association field. See worked example `42-metadata-metric-id-to-entities` for the metricId → entity round-trip. |
63
63
  | `ATTRIBUTE` | `{ op: "ATTRIBUTE", expressions: [...] }` | By metric-attribute expression |
64
64
  | `GROUP` | `{ op: "GROUP", id?, managementModuleId? }` | By management module / group |
65
65
  | `SERVICE` | `{ op: "SERVICE", values: [...] }` | By named service |
@@ -26,7 +26,7 @@ flowchart LR
26
26
  src["source: FROM_TOPOLOGY / FROM_METADATA / FROM / FROM_DATA"] --> joins["joins (optional): JOIN_TOPOLOGY / JOIN_METADATA / JOIN_DATA"]
27
27
  joins --> transforms["transforms: GROUP / FILTER / WINDOW / MAP_STRING / FORMAT_TIME / DISTINCT / ORDER"]
28
28
  transforms --> aggs["aggregations: COUNT / SUM / MEAN / TOP / BOTTOM / FIRST / LAST / QUANTILE / AGG"]
29
- aggs --> shape["shape: KEEP (always last)"]
29
+ aggs --> shape["shape: KEEP (optional; last if used)"]
30
30
  ```
31
31
 
32
32
  Mental model: a **dataframe pipeline**. Source ops produce rows with named
@@ -186,6 +186,14 @@ After a `FROM_TOPOLOGY` source, join in metric metadata for those vertices.
186
186
  `joinType`: `INNER` (default) | `LEFT`. Use `LEFT` when you want to keep
187
187
  vertices that have no metrics (rows with null metric columns are retained).
188
188
 
189
+ **Scope to one entity (metric inventory for a named vertex).** A common
190
+ recipe — `FROM_TOPOLOGY` filtered to one `externalId` with `VERTEX`,
191
+ followed by `JOIN_METADATA(ALL)` and `KEEP vertex.externalId, metric.id`
192
+ — enumerates every metric an entity emits. Worked example:
193
+ `34-nassql-entity-to-metric-ids`. The inverse direction (metricId →
194
+ the externalIds of associated entities) is `42-metadata-metric-id-to-entities`,
195
+ via the `metric.attributes.external_ids` array on every metric descriptor.
196
+
189
197
  ### `JOIN_TOPOLOGY`
190
198
 
191
199
  Inverse: after a metadata source, join the topology for the metrics' source
@@ -260,9 +268,41 @@ Filter rows by a `QueryFilterPredicateSpec`. Predicate ops:
260
268
 
261
269
  String-based filter expression for cases the predicate spec cannot express.
262
270
 
271
+ Operators: `&&` (AND), `||` (OR), `!` (NOT), `== != < <= > >=`, `matches` (regex). String literals use single quotes.
272
+
273
+ The visual editor's FILTER_EXPR helper offers these same examples as one-click templates — keep them synchronized when either side gains a new pattern. (Longer-term, the editor should load examples from this file rather than maintaining its own list.)
274
+
275
+ Worked examples:
276
+
277
+ ```json
278
+ { "op": "FILTER_EXPR", "spec": "metric_count > 100" }
279
+ ```
280
+ *Numeric threshold — keep rows where a numeric column passes a threshold.*
281
+
282
+ ```json
283
+ { "op": "FILTER_EXPR", "spec": "metric.source matches '.*prod.*'" }
284
+ ```
285
+ *String regex on a metric attribute — anchor with `^…$` for a full match; otherwise treated as substring.*
286
+
263
287
  ```json
264
288
  { "op": "FILTER_EXPR", "spec": "metric_count > 100 && metric.source matches '.*prod.*'" }
265
289
  ```
290
+ *Compound (AND) — combine clauses with `&&` (AND) or `||` (OR).*
291
+
292
+ ```json
293
+ { "op": "FILTER_EXPR", "spec": "used > capacity * 0.9" }
294
+ ```
295
+ *Column-vs-column — both sides can reference columns and arithmetic.*
296
+
297
+ ```json
298
+ { "op": "FILTER_EXPR", "spec": "!(metric.source matches '.*staging.*')" }
299
+ ```
300
+ *Negation — wrap any expression with `!(…)` to negate it.*
301
+
302
+ ```json
303
+ { "op": "FILTER_EXPR", "spec": "metric.environment == 'prod'" }
304
+ ```
305
+ *String equality — single quotes for string literals; `==` for equality, `!=` for inequality.*
266
306
 
267
307
  ### `ORDER`
268
308
 
@@ -28,7 +28,7 @@ The `FROM_METADATA` vs `FROM` confusion is the most-confused single distinction
28
28
 
29
29
  ## Schema landmines (READ FIRST)
30
30
 
31
- 1. **`KEEP` must be the LAST op.** Anything after KEEP returns HTTP 400. Put projection at the end of the pipeline; do GROUP / FILTER / sorting before.
31
+ 1. **`KEEP`, when used, must be the LAST op.** KEEP itself is **optional** — many pipelines end with `AGG` / `COUNT` / `TOP` / `BOTTOM` / `DESCRIBE` and have no KEEP at all. But anything that follows a KEEP returns HTTP 400. If you do project, put KEEP at the end and do GROUP / FILTER / sorting before it.
32
32
  2. **`TOP` and `BOTTOM` use `n`, not `count`.** `{op:"TOP", column:"x", n:10}` — schema field is exactly `n`. The intuitive `count: 10` parses but rejects.
33
33
  3. **`GROUP`, `ORDER`, `KEEP` cannot have empty `columns`.** Schema enforces `.min(1)`. Same for `FILTER`'s `AND`/`OR` `spec` arrays.
34
34
  4. **`JOIN_TOPOLOGY` does NOT take a `querySpecifier`** — it joins by pipeline data via `externalIdColumn`, not by a TAS specifier. Surprising compared to JOIN_METADATA.
@@ -39,6 +39,8 @@ The `FROM_METADATA` vs `FROM` confusion is the most-confused single distinction
39
39
  9. **TOP and BOTTOM have asymmetric `sortAscending` defaults.** TOP defaults to `false` (largest first); BOTTOM defaults to `true` (smallest first). Setting `sortAscending: true` on TOP is legal but produces BOTTOM-equivalent ordering — switch the op for clarity.
40
40
  10. **`GROUP` only drops non-grouped columns when followed by an aggregator.** `GROUP → AGG` collapses to grouping-key + `as` outputs; `GROUP → BOTTOM/TOP/ORDER/KEEP/FILTER` (no aggregator) preserves the upstream column shape. Use `KEEP` after GROUP if you want pruning without aggregation.
41
41
  11. **Diagnostic: verify a FROM matches anything before pulling on the full pipeline.** Run `[FROM, KEEP({columns:["metric.id"]})]` in isolation; zero data rows → the specifier matches no metric in this tenant. The data-store editor's "Run JUST this step" control on FROM / FROM_METADATA does this for you.
42
+ 12. **`COUNT` after `GROUP` requires an explicit `as`.** Schema marks `as` optional with default `"count"`, but the server returns HTTP 400 for a bare `{op:"COUNT"}` immediately after `GROUP`. Always set `as` on COUNT (e.g. `{op:"COUNT", as:"n"}`). Treat `as` as required in practice. See `cookbooks/gotchas` for the full schema-vs-server discrepancy note.
43
+ 13. **TAS ATTRIBUTE expression `name` is `type`, NOT `_type`.** `_type` is a storage-side / NASSQL specifier convention; using it inside a TAS ATTRIBUTE filter is accepted by the schema (the schema allows any string) but silently returns zero rows. Same for `hostname`, `agentType`, etc. — use the user-facing attribute key, not the storage field. See `cookbooks/gotchas`.
42
44
 
43
45
  ## The envelope
44
46
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dx-do/cli",
3
- "version": "6.0.1",
3
+ "version": "6.0.4",
4
4
  "description": "CLI execution of DX Operational Observability operations and triage",
5
5
  "author": "Ki Alam",
6
6
  "logo": {