@uptimizr/agent-core 1.1.1 → 1.2.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/AGENTS.md +221 -34
- package/README.md +57 -21
- package/dist/client.d.ts +22 -5
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +39 -10
- package/dist/client.js.map +1 -1
- package/dist/context.d.ts +93 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +137 -0
- package/dist/context.js.map +1 -0
- package/dist/index.d.ts +10 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +20 -2
- package/dist/index.js.map +1 -1
- package/dist/nonRegistryTools.d.ts +34 -0
- package/dist/nonRegistryTools.d.ts.map +1 -0
- package/dist/nonRegistryTools.js +70 -0
- package/dist/nonRegistryTools.js.map +1 -0
- package/dist/prompt.d.ts +33 -0
- package/dist/prompt.d.ts.map +1 -0
- package/dist/prompt.js +49 -0
- package/dist/prompt.js.map +1 -0
- package/dist/provider.d.ts +18 -0
- package/dist/provider.d.ts.map +1 -1
- package/dist/providers/anthropic.d.ts +18 -1
- package/dist/providers/anthropic.d.ts.map +1 -1
- package/dist/providers/anthropic.js +35 -3
- package/dist/providers/anthropic.js.map +1 -1
- package/dist/providers/openai.d.ts +14 -1
- package/dist/providers/openai.d.ts.map +1 -1
- package/dist/providers/openai.js +23 -1
- package/dist/providers/openai.js.map +1 -1
- package/dist/queryTool.d.ts +36 -0
- package/dist/queryTool.d.ts.map +1 -0
- package/dist/queryTool.js +123 -0
- package/dist/queryTool.js.map +1 -0
- package/dist/registryTools.d.ts +23 -1
- package/dist/registryTools.d.ts.map +1 -1
- package/dist/registryTools.js +185 -31
- package/dist/registryTools.js.map +1 -1
- package/dist/skills.d.ts +105 -0
- package/dist/skills.d.ts.map +1 -0
- package/dist/skills.generated.d.ts +44 -0
- package/dist/skills.generated.d.ts.map +1 -0
- package/dist/skills.generated.js +511 -0
- package/dist/skills.generated.js.map +1 -0
- package/dist/skills.js +144 -0
- package/dist/skills.js.map +1 -0
- package/dist/tools.d.ts +55 -8
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +41 -1
- package/dist/tools.js.map +1 -1
- package/dist/writeTools.d.ts +68 -0
- package/dist/writeTools.d.ts.map +1 -0
- package/dist/writeTools.js +256 -0
- package/dist/writeTools.js.map +1 -0
- package/llms.txt +148 -20
- package/package.json +7 -5
- package/skills/attention-hotspots/SKILL.md +88 -0
- package/skills/conversion-investigation/SKILL.md +97 -0
- package/skills/performance-regression-triage/SKILL.md +106 -0
- package/skills/weekly-scene-health/SKILL.md +105 -0
- package/skills/xr-comfort-audit/SKILL.md +95 -0
package/AGENTS.md
CHANGED
|
@@ -9,17 +9,25 @@ The **framework-agnostic, browser-safe core** shared by every Uptimizr analytics
|
|
|
9
9
|
the agent tool surface **once** (ADR 0050 §1) so `@uptimizr/mcp`, the dashboard assistant, and the
|
|
10
10
|
demo assistant never drift apart. It owns:
|
|
11
11
|
|
|
12
|
-
- the **read-only
|
|
13
|
-
**generated** from the `@uptimizr/metrics` semantic metric registry (ADR 0051 §1), so
|
|
14
|
-
collector's read surface cannot drift;
|
|
12
|
+
- the **read-only analytics catalog** (`readTools`) — one entry per documented collector query
|
|
13
|
+
endpoint, **generated** from the `@uptimizr/metrics` semantic metric registry (ADR 0051 §1), so
|
|
14
|
+
coverage of the collector's read surface cannot drift;
|
|
15
|
+
- the **project-metadata catalog** (`writeTools`) — `annotate`, `define_term`, `save_analysis`
|
|
16
|
+
(ADR 0051 §5), `pin_panel` and `unpin_panel` (ADR 0051 §7), plus the four reads that make them
|
|
17
|
+
usable (`list_annotations`, `list_glossary`, `list_analyses`, `list_panels`). Deliberately a
|
|
18
|
+
**separate export**, never folded into `readTools`, so an integration's read-only stance stays
|
|
19
|
+
inspectable at a glance;
|
|
15
20
|
- a headless **LLM provider-adapter interface** (`LlmProvider`) — messages + tool schemas in, tool
|
|
16
21
|
calls or final text out;
|
|
17
22
|
- the headless **tool-calling loop** (`runAgent`) — LLM ↔ tools ↔ collector.
|
|
18
23
|
|
|
19
|
-
The core ships **no model and no key**. It only ever
|
|
20
|
-
`CollectorClient` (`
|
|
24
|
+
The core ships **no model and no key**. It only ever talks to a consumer's **own** collector via the
|
|
25
|
+
`CollectorClient` (`x-api-key`). **Events are read-only**: nothing in this package can write, alter
|
|
26
|
+
or delete an analytics event, and there is no ingestion path. The client's `post`/`put`/`delete`
|
|
27
|
+
methods exist for the metadata write tools alone, and the collector gates those behind the
|
|
28
|
+
`annotate` capability (ADR 0051 §9).
|
|
21
29
|
|
|
22
|
-
##
|
|
30
|
+
## Analytics tool catalog (read-only)
|
|
23
31
|
|
|
24
32
|
<!-- generated:registry-tool-names:start — generated by `pnpm gen:docs`; edit the metric registry, not this list -->
|
|
25
33
|
|
|
@@ -29,36 +37,94 @@ The core ships **no model and no key**. It only ever reads a consumer's **own**
|
|
|
29
37
|
`position_heatmap`, `session_trajectory`, `aggregate_paths`, `scene_coverage`, `camera_distance`,
|
|
30
38
|
`click_rays`, `flow_links`, `top_meshes`, `mesh_sources`, `mesh_trend`, `mesh_dwell`,
|
|
31
39
|
`mesh_blind_spots`, `mesh_interaction_kinds`, `mesh_reachability`, `dead_clicks`, `rage_clicks`,
|
|
32
|
-
`hover_dwell`, `interaction_sources`, `top_input_actions`, `
|
|
33
|
-
`
|
|
34
|
-
`
|
|
35
|
-
`
|
|
36
|
-
`
|
|
37
|
-
`
|
|
38
|
-
`
|
|
39
|
-
`
|
|
40
|
-
`scene_retention`, `load_bounce_funnel`, `variant_leaderboard
|
|
40
|
+
`hover_dwell`, `interaction_sources`, `top_input_actions`, `custom_event_vocabulary`,
|
|
41
|
+
`camera_gestures`, `navigation_stats`, `backtrack_ratio`, `perf_summary`, `render_scale_truth`,
|
|
42
|
+
`perf_distribution`, `fps_histogram`, `frame_time_percentiles`, `jank_rate`, `perf_churn`,
|
|
43
|
+
`perf_by_device`, `perf_by_scene`, `perf_heatmap`, `compile_stalls`, `resource_summary`,
|
|
44
|
+
`resource_percentiles`, `stability_counts`, `graphics_diagnostics`, `error_heatmap`,
|
|
45
|
+
`rendering_technology`, `capability_changes`, `xr_rotation`, `xr_sources`, `xr_abandonment`,
|
|
46
|
+
`xr_locomotion`, `xr_tracking_quality`, `boundary_heatmap`, `boundary_heatmap_stats`,
|
|
47
|
+
`xr_boundary_contacts`, `ar_placement_time_to_place`, `ar_placement_attempts`,
|
|
48
|
+
`ar_placement_surfaces`, `funnel`, `scene_retention`, `load_bounce_funnel`, `variant_leaderboard`,
|
|
49
|
+
`insight_baseline`, `insight_movers`, `insight_anomalies`, `insight_significance`,
|
|
50
|
+
`insight_scene_health`
|
|
51
|
+
|
|
52
|
+
Only on a key holding `query:raw`, and only when the collector runs with
|
|
53
|
+
`ENABLE_RAW_SESSION_RETENTION` (ADR 0003):
|
|
54
|
+
|
|
55
|
+
`session_narrative`
|
|
41
56
|
|
|
42
57
|
<!-- generated:registry-tool-names:end -->
|
|
43
58
|
|
|
44
59
|
Each name is a metric in the collector's semantic metric registry (ADR 0051) and maps one-to-one to
|
|
45
60
|
a documented query endpoint. Most accept `since`/`until` (epoch ms) plus endpoint-specific filters.
|
|
46
61
|
|
|
62
|
+
## The `query` tool (the query DSL)
|
|
63
|
+
|
|
64
|
+
One more tool sits after the generated list, and it is **not** per-metric: `query` (ADR 0051 §3).
|
|
65
|
+
Its input _is_ the query DSL, so it can run any metric above with any filter that metric declares:
|
|
66
|
+
|
|
67
|
+
```jsonc
|
|
68
|
+
{
|
|
69
|
+
"v": 1,
|
|
70
|
+
"metric": "mesh_sources",
|
|
71
|
+
"range": { "since": 1757000000000, "until": 1757600000000 },
|
|
72
|
+
"filters": { "scene": "lobby", "cameraMode": "first-person" },
|
|
73
|
+
"limit": 20,
|
|
74
|
+
"format": "summary",
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
- **`range` is required** — both ends, epoch ms. There is no unbounded query.
|
|
79
|
+
- **`format` defaults to `table` here**, not `full`: this is a new, agent-facing surface, so the
|
|
80
|
+
envelope with the sample size and the truncation flag is the default.
|
|
81
|
+
- The grammar is **closed**. Metrics, dimensions and filters are exactly the registry's vocabulary;
|
|
82
|
+
naming something outside it returns a `400` whose `issues[].accepted` lists what would have
|
|
83
|
+
worked. Read that rather than guessing again.
|
|
84
|
+
- `dimensions` may be any subset a metric declares **when** its measure is a portable count —
|
|
85
|
+
event counts, mesh and interaction tallies, input actions, camera gestures. A spatial heatmap or a
|
|
86
|
+
percentile is computed at one fixed grain and refuses anything else, naming the grain it supports.
|
|
87
|
+
- **`compare`** — another `{ range }` or `{ segment }`; the result comes back joined on the
|
|
88
|
+
dimension key as `{ current, previous, delta, deltaPct }`, with a significance test where the
|
|
89
|
+
measure is a count and both windows clear the metric's minimum. Never subtract two results by hand.
|
|
90
|
+
- **`explain: true`** — the compiled plan instead of the rows: the tier, the SQL with its parameters
|
|
91
|
+
left unbound, `params` by name and type (never value), `rowsScanned`, and `warnings` (a capture
|
|
92
|
+
channel that produced nothing, a sample below the metric's minimum, a truncated result).
|
|
93
|
+
- **`drillQuery`** — every row of a `summary` carries the whole query narrowed to that row, ready to
|
|
94
|
+
send straight back.
|
|
95
|
+
- `order` takes a measure column, and only where the result is a ranked list.
|
|
96
|
+
|
|
97
|
+
It reaches the collector as `GET /api/v1/query?q=<url-encoded JSON>`, so `CollectorClient` stays
|
|
98
|
+
`GET`-only — an Uptimizr agent remains structurally incapable of writing.
|
|
99
|
+
|
|
100
|
+
## Non-metric reads
|
|
101
|
+
|
|
102
|
+
`readTools` is generated from the metric registry, with one deliberate exception: collector reads
|
|
103
|
+
that are **configuration rather than measurements** and so have no registry entry
|
|
104
|
+
(`nonRegistryTools.ts`). Today that is `list_subscriptions` (ADR 0051 §6) — the project's
|
|
105
|
+
standing conditional subscriptions, what they watch for and how each last went. It takes no
|
|
106
|
+
arguments and no time range. Creating or deleting a subscription needs the `annotate` capability
|
|
107
|
+
and is not available as a tool; do it over plain HTTP.
|
|
108
|
+
|
|
47
109
|
## Result formats (`format`)
|
|
48
110
|
|
|
49
111
|
Every **aggregate** tool in the catalog declares a `format` argument — `full | table | summary`.
|
|
50
112
|
It picks the envelope the rows arrive in and filters nothing (ADR 0051 §2). The collector's
|
|
51
113
|
`preSerialization` hook does the shaping, so the tool definitions stay pure `buildRequest` functions.
|
|
52
114
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
| `
|
|
115
|
+
**`buildRequest` applies `DEFAULT_TOOL_FORMAT` (`table`) when the caller names no format** and
|
|
116
|
+
sends it explicitly, so the collector's own default stays `full` and the dashboard is unaffected.
|
|
117
|
+
A tool's `outputSchema` describes all three envelopes, so whichever one comes back validates.
|
|
118
|
+
|
|
119
|
+
| `format` | Returns | Use it when |
|
|
120
|
+
| --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
|
|
121
|
+
| `summary` | A bounded digest — `ranked` top rows, a `series` trend, merged spatial `clusters` or a single `record` — with shares, a sample size, the metric's `caveats` and a templated `reading` sentence. | **Ask for it** whenever a model reads the result and you do not need every row. |
|
|
122
|
+
| `table` | `{ meta, rows }`: the rows plus metric, range, applied filters, sample size, row count, a truncation flag and the registry's limits. | **The default** — every row _and_ the context to judge it. |
|
|
123
|
+
| `full` | The bare rows, unchanged, with no envelope at all. | You are post-processing the rows yourself. |
|
|
58
124
|
|
|
59
125
|
- `summary` is capped at the metric's `limits.maxSummaryRows`, which is what makes a heatmap, voxel
|
|
60
126
|
cloud or long leaderboard safe to hand a model at all. This matters most for the **local** backend:
|
|
61
|
-
a 4-bit 7–8B model's whole context can be filled by one `full`
|
|
127
|
+
a 4-bit 7–8B model's whole context can be filled by one heatmap's rows, in `full` or in `table`.
|
|
62
128
|
- `reading` and `caveats` are templated from the registry's column semantics by pure code — no model
|
|
63
129
|
— so identical rows always produce identical words. Quote them rather than re-deriving them.
|
|
64
130
|
- Shares appear only where the measure can honestly be summed; an FPS or ratio metric reports
|
|
@@ -66,17 +132,90 @@ It picks the envelope the rows arrive in and filters nothing (ADR 0051 §2). The
|
|
|
66
132
|
`cellSize` for world space.
|
|
67
133
|
- `session_meta` and `scene_representation` are single stored records, not aggregations, and declare
|
|
68
134
|
no `format`.
|
|
69
|
-
- `
|
|
70
|
-
|
|
71
|
-
|
|
135
|
+
- The **tools** default to `table`; the collector still defaults to `full`, so a consumer that calls
|
|
136
|
+
the HTTP endpoint directly (the dashboard) is unaffected. `table` is self-describing but not
|
|
137
|
+
bounded — its `meta` costs a fixed ~200 characters and the rows are still all of them — so pass
|
|
138
|
+
`format: "summary"` when the result could be large.
|
|
139
|
+
|
|
140
|
+
## Project context (ADR 0051 §5)
|
|
141
|
+
|
|
142
|
+
Read `GET /api/v1/context` **before** the first question and put a rendering of it in the system
|
|
143
|
+
prompt. It is the only read that describes the project rather than the API: the real scene ids and
|
|
144
|
+
named region ids, the custom events the application emits with their `props` keys and coarse types,
|
|
145
|
+
top meshes, bound input actions, data freshness, retention flags, and `metrics.disabledByCapture` —
|
|
146
|
+
metrics that return empty because their capture channel is off, not because nothing happened.
|
|
147
|
+
|
|
148
|
+
`renderContextForPrompt(document)` turns it into a compact block (~1.5 k characters, capped and
|
|
149
|
+
truncated on a line boundary) that a 1–3 B local model can carry alongside the tool schemas. It is
|
|
150
|
+
pure, tolerant of a partial or unfamiliar document, and returns `""` when there is nothing to say —
|
|
151
|
+
so a collector too old to serve the endpoint degrades to the prompt you had before rather than
|
|
152
|
+
failing. Append it unconditionally.
|
|
153
|
+
|
|
154
|
+
## Packaged methodology skills (ADR 0051 §7)
|
|
155
|
+
|
|
156
|
+
A prompt names tools; a **skill** carries the method. Each one is an Agent Skills file,
|
|
157
|
+
`skills/<name>/SKILL.md`, shipped in this tarball: frontmatter declaring the tools it relies on,
|
|
158
|
+
the key capabilities it needs and the arguments it takes, and a body of numbered steps — orient on
|
|
159
|
+
`uptimizr://context`, start from the score, test the change before calling it real, date it, and
|
|
160
|
+
end with recommendations plus something worth keeping. Read one before you improvise:
|
|
161
|
+
`skills/weekly-scene-health/SKILL.md`.
|
|
162
|
+
|
|
163
|
+
<!-- generated:registry-skill-names:start — generated by `pnpm gen:docs`; edit the SKILL.md files, not this list -->
|
|
164
|
+
|
|
165
|
+
- `attention_hotspots` (scene (required), range) — Find where visitors look and click in a scene: view-direction concentration, gaze→mesh flow, the objects that draw the most interaction, and the ones nobody ever notices. USE FOR: deciding where to put a call to action, finding ignored or invisible content, explaining why an object gets no clicks, laying out a scene around what people actually look at.
|
|
166
|
+
Method: `skills/attention-hotspots/SKILL.md`. Tools: `camera_heatmap`, `flow_links`, `click_rays`, `top_meshes`, `mesh_dwell`, `mesh_blind_spots`, `query`.
|
|
167
|
+
- `conversion_investigation` (scene, range) — Find out where a funnel loses people and whether the loss is real: step-by-step drop-off, the bounce that happens before the funnel even starts, scene-to-scene retention, variant performance, and the interaction failures (dead clicks, rage clicks, unreachable meshes) that explain a stalled step. USE FOR: a funnel that converts worse than expected, an A/B variant comparison, "where do people drop off", diagnosing a step nobody completes.
|
|
168
|
+
Method: `skills/conversion-investigation/SKILL.md`. Tools: `funnel`, `load_bounce_funnel`, `scene_retention`, `variant_leaderboard`, `dead_clicks`, `rage_clicks`, `mesh_reachability`, `flow_links`, `insight_significance`, `insight_movers`, `query`.
|
|
169
|
+
- `performance_regression_triage` (scene, range) — Triage a frame-rate or stability regression: confirm it moved, date it, locate it (which scene, device class, place in the scene), and name the mechanism — jank, shader compile stalls, memory pressure, a render-scale change or a rendering-technology shift. USE FOR: "the app got slower", a FPS drop after a release, stutter reports, deciding whether a regression is real or noise.
|
|
170
|
+
Method: `skills/performance-regression-triage/SKILL.md`. Tools: `insight_movers`, `insight_anomalies`, `insight_significance`, `insight_baseline`, `perf_summary`, `perf_distribution`, `frame_time_percentiles`, `jank_rate`, `perf_by_device`, `perf_by_scene`, `perf_heatmap`, `compile_stalls`, `resource_percentiles`, `render_scale_truth`, `rendering_technology`, `query`.
|
|
171
|
+
- `weekly_scene_health` (scene, range) — A weekly health check for a scene (or the whole project): a weighted health score with every factor traced back to the metric behind it, what changed against last week, traffic, event mix, performance, and the most-interacted meshes. USE FOR: the recurring "how is the scene doing?" review, a scheduled weekly or monthly report, a first look at a project you do not know yet, deciding which scene to investigate next.
|
|
172
|
+
Method: `skills/weekly-scene-health/SKILL.md`. Tools: `insight_scene_health`, `insight_movers`, `insight_baseline`, `insight_significance`, `insight_anomalies`, `event_counts`, `timeseries`, `perf_summary`, `top_meshes`, `list_sessions`, `query`.
|
|
173
|
+
- `xr_comfort_audit` (scene, range) — Audit VR/AR comfort for a scene (or the whole project): rapid head rotation, locomotion style, tracking quality, guardian/boundary contacts, input-source mix, and the short sessions that mean someone took the headset off. USE FOR: motion-sickness complaints, immersive sessions that end early, choosing a locomotion scheme, checking whether a play space is big enough.
|
|
174
|
+
Method: `skills/xr-comfort-audit/SKILL.md`. Tools: `xr_rotation`, `xr_locomotion`, `xr_abandonment`, `xr_sources`, `xr_tracking_quality`, `xr_boundary_contacts`, `boundary_heatmap_stats`, `insight_scene_health`, `insight_movers`, `query`.
|
|
175
|
+
|
|
176
|
+
<!-- generated:registry-skill-names:end -->
|
|
177
|
+
|
|
178
|
+
`AGENT_SKILLS` is those files compiled to data (`skills.generated.ts`, by
|
|
179
|
+
`scripts/gen-agent-skills.mjs`) — this package is browser-safe and never reads them from disk at
|
|
180
|
+
runtime. Each entry carries the title, description, tools, capabilities, arguments and a pure
|
|
181
|
+
`render(args)` producing the single user turn. `getAgentSkill(name)` resolves one by either
|
|
182
|
+
spelling (`xr_comfort_audit` or `xr-comfort-audit`), and `xr_comfort_review` still resolves to
|
|
183
|
+
`xr_comfort_audit` for clients that saved the pre-#316 name.
|
|
184
|
+
|
|
185
|
+
Three consumers drive the same text and must not drift: `@uptimizr/mcp` registers each as an MCP
|
|
186
|
+
prompt template, `uptimizr agent report --skill` seeds its headless transcript with it, and the
|
|
187
|
+
assistant offers it as a starter prompt. **Reword the SKILL.md and run `pnpm gen:skills`** — never
|
|
188
|
+
copy a skill's text anywhere else.
|
|
189
|
+
|
|
190
|
+
## Shared system-prompt fragments
|
|
191
|
+
|
|
192
|
+
`ANALYTICS_AGENT_GUIDELINES` is what every analytics agent says about the _data_ — figures come
|
|
193
|
+
from tools and are never invented, everything is aggregate and privacy-preserving, timestamps are
|
|
194
|
+
epoch milliseconds, a tool error is explained rather than papered over. `renderCurrentTimeLine(nowMs)`
|
|
195
|
+
gives the model a clock so it can resolve "this week" into `since`/`until`. The browser assistant
|
|
196
|
+
and the report CLI both build their system prompt from these plus their own role sentence; only the
|
|
197
|
+
role and the output format legitimately differ.
|
|
72
198
|
|
|
73
199
|
## Rules for agents
|
|
74
200
|
|
|
75
|
-
- **
|
|
76
|
-
|
|
77
|
-
ADR 0017). A new tool = a new
|
|
78
|
-
|
|
79
|
-
in the collector,
|
|
201
|
+
- **Events are read-only; privacy-preserving throughout.** Never add an ingestion tool, an
|
|
202
|
+
event-mutating tool, or a raw per-session event tool. The analytics surface is aggregate-only and
|
|
203
|
+
no data leaves the consumer's infrastructure (ADR 0003 / ADR 0017). A new analytics tool = a new
|
|
204
|
+
**metric registry entry** in `@uptimizr/metrics` for a documented query endpoint — never a
|
|
205
|
+
hand-written catalog entry here — and no aggregation/business logic (that lives in the collector,
|
|
206
|
+
ADR 0005).
|
|
207
|
+
- **Writes stay in `writeTools`, and stay metadata.** The only writable surface is project metadata
|
|
208
|
+
(annotations, glossary, saved analyses, pinned panels), each call needs an `annotate` key, each is
|
|
209
|
+
bounded at the collector's edge and audited there. Never add a write tool to `readTools`, and
|
|
210
|
+
never add one that reaches a non-metadata endpoint.
|
|
211
|
+
- **A pinned panel is metadata too.** `pin_panel` stores a **spec** — a metric id, a chart name,
|
|
212
|
+
some column names — that the dashboard renders with panels it already ships; no module is loaded
|
|
213
|
+
and nothing is evaluated, so pinning does not widen the dashboard's trust boundary (ADR 0041).
|
|
214
|
+
Send the same `query` document the `query` tool takes, with `range: "inherit"` so the panel
|
|
215
|
+
follows the dashboard's own time filter, and a `chart` the metric's grain supports — the collector
|
|
216
|
+
refuses the rest and names the charts that would have worked. Read `list_panels` before pinning,
|
|
217
|
+
so you extend the dashboard rather than duplicate it; `unpin_panel` removes a panel for
|
|
218
|
+
**everyone** on the project, so never unpin one you have not read.
|
|
80
219
|
- **Browser-safe.** No Node dependencies, no `types: ["node"]`. At runtime this package uses `zod`
|
|
81
220
|
plus the pure, data-only `@uptimizr/metrics` package — **never** `@uptimizr/db`, which owns the
|
|
82
221
|
DuckDB store and its ~37 MB native binding. `src/__tests__/browserSafety.test.ts` bundles the
|
|
@@ -84,21 +223,69 @@ It picks the envelope the rows arrive in and filters nothing (ADR 0051 §2). The
|
|
|
84
223
|
if `@uptimizr/db` (or anything else with a native/optional binary dependency) reappears in the
|
|
85
224
|
manifest. Anything that needs `process.env`, stdio, or the filesystem belongs in a consumer
|
|
86
225
|
package (e.g. `@uptimizr/mcp`), not here.
|
|
226
|
+
- **Start from an insight primitive on an open-ended question.** "How are things?" does not mean
|
|
227
|
+
"call thirty tools": `insight_scene_health` says _which scene_ to look at (six weighted factors,
|
|
228
|
+
each naming the metric, raw value and project baseline behind it; 50 is the project norm, not a
|
|
229
|
+
pass mark), `insight_movers` compares every comparable metric with the previous equal window and
|
|
230
|
+
ranks the changes by how unusual each is, and `insight_baseline` says whether a level is outside
|
|
231
|
+
normal for that scene. Two fields decide whether a mover is reportable: `direction` is the
|
|
232
|
+
registry's opinion of what a _rise_ means (so a rise in a `down` metric is a regression, not an
|
|
233
|
+
improvement), and `aboveMinSample: false` means the delta is arithmetic but not evidence — those
|
|
234
|
+
rows are returned rather than dropped, and must never be reported as findings.
|
|
235
|
+
- **Then `insight_anomalies` to put a date on it.** `insight_movers` compares two windows you
|
|
236
|
+
chose; `insight_anomalies` walks one metric's whole series and names the buckets that do not
|
|
237
|
+
belong — `spike` / `drop` for a single bucket far from the ones before it, and `shift` at the
|
|
238
|
+
bucket where the level moved and _stayed_ moved, which is the shape a release regression has and
|
|
239
|
+
the one no per-bucket threshold can see. Quote `bucketStart`, and read `contributor`: where the
|
|
240
|
+
metric declares a dimension it can be split by, the row names the mesh, source, input action,
|
|
241
|
+
event type or scene holding the largest share of the excess. Its `z` is in standard deviations
|
|
242
|
+
while `insight_movers`' is the same ratio unscaled, so the two columns are not comparable.
|
|
243
|
+
- **Call `insight_significance` before calling a single change real.** It reports the effect, a 95%
|
|
244
|
+
interval and a p-value for one metric across two windows, with the test chosen from what the
|
|
245
|
+
measure is. Read `ci95` before `p` — an interval straddling 0 means you cannot tell yet — and
|
|
246
|
+
read `powerNote`, which distinguishes "no effect" from "not enough data". It compares two
|
|
247
|
+
_windows_, not two segments.
|
|
87
248
|
- Tool definitions are pure (`buildRequest`) and must stay unit-testable without a live collector.
|
|
88
249
|
- The 20 tool names (and argument schemas) that shipped before the registry are a public contract:
|
|
89
250
|
`src/__tests__/shippedToolCompat.test.ts` pins them against a frozen fixture. Widening a tool with
|
|
90
251
|
a new **optional** argument is fine; renaming one or making an argument required is not.
|
|
91
252
|
- Keep provider adapters thin and out of this package: implement `LlmProvider` in the consumer.
|
|
253
|
+
- `ProviderResponse.usage` is **optional on every field**: a provider may report neither, one or
|
|
254
|
+
both token counts, and a local backend reports nothing. Treat a missing number as "not reported",
|
|
255
|
+
never as zero — a cost line that prints `0` when nothing was measured is a lie.
|
|
92
256
|
|
|
93
257
|
## Programmatic API
|
|
94
258
|
|
|
95
|
-
`readTools`, `coreReadTools`, `selectReadTools(kind)`, `filterReadTools(names)`,
|
|
259
|
+
`readTools`, `rawTools`, `coreReadTools`, `selectReadTools(kind)`, `filterReadTools(names)`,
|
|
96
260
|
`registryToTools(metrics?)`, `createCollectorClient(config)`, `toToolSchemas(tools?)`,
|
|
97
|
-
`runAgent(options)`,
|
|
98
|
-
`
|
|
261
|
+
`runAgent(options)`, `renderContextForPrompt(context, nowMs?)`, `AGENT_SKILLS` /
|
|
262
|
+
`getAgentSkill(name)` / `renderAgentSkill(name, args)`, `ANALYTICS_AGENT_GUIDELINES` /
|
|
263
|
+
`renderCurrentTimeLine(nowMs)`, plus the `LlmProvider` / `AgentMessage` / `AgentToolCall` /
|
|
264
|
+
`ProviderResponse` / `ProviderUsage` / `AgentSkill` / `PromptContextDocument` types.
|
|
265
|
+
|
|
266
|
+
Metadata writes (ADR 0051 §5/§7): `writeTools`, `mutatingWriteTools`, the individual
|
|
267
|
+
`annotateTool` / `defineTermTool` / `saveAnalysisTool` / `pinPanelTool` / `unpinPanelTool` /
|
|
268
|
+
`listAnnotationsTool` / `listGlossaryTool` / `listAnalysesTool` / `listPanelsTool`, the `WriteTool`
|
|
269
|
+
type and `WriteNotSupportedError` (thrown when a hand-built read-only client has no write
|
|
270
|
+
transport). Each has an `execute(client, args)` rather than a `buildRequest`, because a write is one
|
|
271
|
+
call rather than a request description. `pin_panel` and `unpin_panel` mutate; `list_panels` does
|
|
272
|
+
not, and `mutatingWriteTools` is the filtered view. `unpin_panel` is the only tool here that uses
|
|
273
|
+
`CollectorClient.delete`.
|
|
274
|
+
|
|
275
|
+
The catalog is 77 tools on a plain `query` key. A small local model cannot hold every schema in its
|
|
276
|
+
function-calling prompt — hand a run `coreReadTools` or `filterReadTools([...])` rather than the
|
|
277
|
+
full catalog.
|
|
278
|
+
|
|
279
|
+
### Capability-gated tools
|
|
99
280
|
|
|
100
|
-
|
|
101
|
-
|
|
281
|
+
`readTools` is the **`query`** surface — every tool a plain read key may call. `rawTools` is
|
|
282
|
+
generated from the registry metrics whose endpoint declares `capability: "query:raw"` (today just
|
|
283
|
+
`session_narrative`, the compacted account of one session) and is kept separate on purpose: the
|
|
284
|
+
capability belongs to the _key the agent was handed_, not to the agent. Register it only after
|
|
285
|
+
confirming the key holds `query:raw` via `GET /api/v1/whoami` — a tool that always answers `403`
|
|
286
|
+
costs a model a turn and invites a retry. The collector refuses those endpoints unless it also runs
|
|
287
|
+
with `ENABLE_RAW_SESSION_RETENTION`, so the capability is necessary but never sufficient
|
|
288
|
+
(ADR 0003 / ADR 0051 §7).
|
|
102
289
|
|
|
103
290
|
## More
|
|
104
291
|
|
package/README.md
CHANGED
|
@@ -3,21 +3,27 @@
|
|
|
3
3
|
The **framework-agnostic, browser-safe core** for Uptimizr analytics agents. It defines the agent
|
|
4
4
|
tool surface **once** so every consumer — the [`@uptimizr/mcp`](../mcp/README.md) server, the
|
|
5
5
|
dashboard assistant, and the backend-less demo assistant — asks the same questions of the same
|
|
6
|
-
|
|
6
|
+
collector API (ADR 0050 §1).
|
|
7
7
|
|
|
8
|
-
It owns
|
|
8
|
+
It owns four things:
|
|
9
9
|
|
|
10
|
-
1. **The read-only
|
|
10
|
+
1. **The read-only analytics catalog** (`readTools`) — one entry per documented aggregate query
|
|
11
11
|
endpoint. Pure Zod shapes, no Node dependencies, unit-testable without a live collector.
|
|
12
|
-
2. **
|
|
12
|
+
2. **The project-metadata catalog** (`writeTools`) — `annotate`, `define_term`, `save_analysis` and
|
|
13
|
+
their three read siblings (ADR 0051 §5). A deliberately **separate** export, never folded into
|
|
14
|
+
`readTools`, so an integration's read-only stance stays inspectable at a glance.
|
|
15
|
+
3. **A headless LLM provider-adapter interface** (`LlmProvider`) — send messages + tool schemas,
|
|
13
16
|
receive tool calls or a final answer. The core ships **no model and no key**; adapters
|
|
14
17
|
(WebLLM/WebGPU, an OpenAI-compatible or Anthropic endpoint) are user-selected and
|
|
15
18
|
user-controlled (ADR 0050 §4).
|
|
16
|
-
|
|
19
|
+
4. **The headless tool-calling loop** (`runAgent`) — drives the LLM ↔ tools ↔ collector round trip,
|
|
17
20
|
framework-agnostic and runnable in a browser, a Node service, a CLI, or a bot.
|
|
18
21
|
|
|
19
|
-
|
|
20
|
-
|
|
22
|
+
**Events are read-only**: nothing here can write, alter or delete an analytics event, and there are
|
|
23
|
+
no ingestion or raw per-session event tools (ADR 0003 / ADR 0017 / ADR 0051 §9). The agent loop
|
|
24
|
+
issues collector `GET`s only. The metadata tools are the single writable surface — they reach three
|
|
25
|
+
metadata endpoints and nothing else, each call needs an `annotate` key, and the collector bounds and
|
|
26
|
+
audits every one.
|
|
21
27
|
|
|
22
28
|
## Install
|
|
23
29
|
|
|
@@ -59,7 +65,7 @@ const result = await runAgent({
|
|
|
59
65
|
console.log(result.content); // the model's final answer
|
|
60
66
|
```
|
|
61
67
|
|
|
62
|
-
##
|
|
68
|
+
## Analytics tool catalog (read-only)
|
|
63
69
|
|
|
64
70
|
<!-- generated:registry-tool-names:start — generated by `pnpm gen:docs`; edit the metric registry, not this table -->
|
|
65
71
|
|
|
@@ -69,21 +75,29 @@ console.log(result.content); // the model's final answer
|
|
|
69
75
|
`position_heatmap`, `session_trajectory`, `aggregate_paths`, `scene_coverage`, `camera_distance`,
|
|
70
76
|
`click_rays`, `flow_links`, `top_meshes`, `mesh_sources`, `mesh_trend`, `mesh_dwell`,
|
|
71
77
|
`mesh_blind_spots`, `mesh_interaction_kinds`, `mesh_reachability`, `dead_clicks`, `rage_clicks`,
|
|
72
|
-
`hover_dwell`, `interaction_sources`, `top_input_actions`, `
|
|
73
|
-
`
|
|
74
|
-
`
|
|
75
|
-
`
|
|
76
|
-
`
|
|
77
|
-
`
|
|
78
|
-
`
|
|
79
|
-
`
|
|
80
|
-
`scene_retention`, `load_bounce_funnel`, `variant_leaderboard
|
|
78
|
+
`hover_dwell`, `interaction_sources`, `top_input_actions`, `custom_event_vocabulary`,
|
|
79
|
+
`camera_gestures`, `navigation_stats`, `backtrack_ratio`, `perf_summary`, `render_scale_truth`,
|
|
80
|
+
`perf_distribution`, `fps_histogram`, `frame_time_percentiles`, `jank_rate`, `perf_churn`,
|
|
81
|
+
`perf_by_device`, `perf_by_scene`, `perf_heatmap`, `compile_stalls`, `resource_summary`,
|
|
82
|
+
`resource_percentiles`, `stability_counts`, `graphics_diagnostics`, `error_heatmap`,
|
|
83
|
+
`rendering_technology`, `capability_changes`, `xr_rotation`, `xr_sources`, `xr_abandonment`,
|
|
84
|
+
`xr_locomotion`, `xr_tracking_quality`, `boundary_heatmap`, `boundary_heatmap_stats`,
|
|
85
|
+
`xr_boundary_contacts`, `ar_placement_time_to_place`, `ar_placement_attempts`,
|
|
86
|
+
`ar_placement_surfaces`, `funnel`, `scene_retention`, `load_bounce_funnel`, `variant_leaderboard`,
|
|
87
|
+
`insight_baseline`, `insight_movers`, `insight_anomalies`, `insight_significance`,
|
|
88
|
+
`insight_scene_health`
|
|
89
|
+
|
|
90
|
+
Only on a key holding `query:raw`, and only when the collector runs with
|
|
91
|
+
`ENABLE_RAW_SESSION_RETENTION` (ADR 0003):
|
|
92
|
+
|
|
93
|
+
`session_narrative`
|
|
81
94
|
|
|
82
95
|
<!-- generated:registry-tool-names:end -->
|
|
83
96
|
|
|
84
97
|
`readTools` is **generated** from the semantic metric registry in `@uptimizr/metrics`
|
|
85
98
|
([ADR 0051](https://github.com/RaananW/Uptimizr/blob/main/docs/adr/0051-ai-first-analytics-layer.md) §1):
|
|
86
|
-
one tool per metric the collector serves on a read endpoint — **
|
|
99
|
+
one tool per metric the collector serves on a read endpoint — **75** on a plain `query` key today,
|
|
100
|
+
plus `session_narrative` behind `query:raw` — covering sessions and
|
|
87
101
|
scenes, pointer/world/gaze/camera heatmaps, mesh attention and blind spots, dead and rage clicks,
|
|
88
102
|
navigation and desire lines, performance (FPS distribution, jank, compile stalls, per-device and
|
|
89
103
|
per-scene), errors and stability, XR/AR comfort and placement, and conversion (funnel, scene
|
|
@@ -100,8 +114,10 @@ Each tool carries:
|
|
|
100
114
|
caveats (sample-size limits, which capture channel must be on);
|
|
101
115
|
- an **input schema** built from the endpoint's filters — most accept `since`/`until` (epoch ms)
|
|
102
116
|
plus `scene`, `session`, `source`, `bins`, `cellSize`, `limit`, `cameraMode`, `region`, …;
|
|
103
|
-
- an **output schema**
|
|
104
|
-
|
|
117
|
+
- an **output schema** derived from the metric's row schema and the `format` result envelopes —
|
|
118
|
+
`{ rows }`, the `{ meta, rows }` table envelope, or a `summary` digest — which `@uptimizr/mcp`
|
|
119
|
+
registers as the MCP `outputSchema`. A tool asks for `table` (`DEFAULT_TOOL_FORMAT`) unless the
|
|
120
|
+
caller names another format.
|
|
105
121
|
|
|
106
122
|
The registry lives in `@uptimizr/metrics`, a dependency-free package (`zod` +
|
|
107
123
|
`@uptimizr/schema`), so installing this one never downloads a database driver: `@uptimizr/db` and
|
|
@@ -109,11 +125,31 @@ its ~37 MB native DuckDB binding are **not** a dependency. Two tests keep that t
|
|
|
109
125
|
asserts no `node:` built-in or DuckDB driver can reach a browser build, and a manifest test fails
|
|
110
126
|
if any package with a native or optional binary dependency reappears.
|
|
111
127
|
|
|
128
|
+
## Methodology skills
|
|
129
|
+
|
|
130
|
+
The packaged investigations (ADR 0051 §7) — the same ones `@uptimizr/mcp` serves as prompt
|
|
131
|
+
templates and `uptimizr agent report --skill` runs headlessly. Each is an Agent Skills file under
|
|
132
|
+
`skills/<name>/SKILL.md` in this package; `AGENT_SKILLS` is those files compiled to data, and
|
|
133
|
+
`getAgentSkill(name).render({ scene })` returns the user turn. `*` marks a required argument.
|
|
134
|
+
|
|
135
|
+
<!-- generated:registry-skills:start — generated by `pnpm gen:docs`; edit the SKILL.md files, not this list -->
|
|
136
|
+
|
|
137
|
+
| Skill | Arguments | What it produces, and when to use it | Tools its method names |
|
|
138
|
+
| ------------------------------- | ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
139
|
+
| `attention_hotspots` | `scene*`, `range?` | Find where visitors look and click in a scene: view-direction concentration, gaze→mesh flow, the objects that draw the most interaction, and the ones nobody ever notices. USE FOR: deciding where to put a call to action, finding ignored or invisible content, explaining why an object gets no clicks, laying out a scene around what people actually look at. | `camera_heatmap`, `flow_links`, `click_rays`, `top_meshes`, `mesh_dwell`, `mesh_blind_spots`, `query` |
|
|
140
|
+
| `conversion_investigation` | `scene?`, `range?` | Find out where a funnel loses people and whether the loss is real: step-by-step drop-off, the bounce that happens before the funnel even starts, scene-to-scene retention, variant performance, and the interaction failures (dead clicks, rage clicks, unreachable meshes) that explain a stalled step. USE FOR: a funnel that converts worse than expected, an A/B variant comparison, "where do people drop off", diagnosing a step nobody completes. | `funnel`, `load_bounce_funnel`, `scene_retention`, `variant_leaderboard`, `dead_clicks`, `rage_clicks`, `mesh_reachability`, `flow_links`, `insight_significance`, `insight_movers`, `query` |
|
|
141
|
+
| `performance_regression_triage` | `scene?`, `range?` | Triage a frame-rate or stability regression: confirm it moved, date it, locate it (which scene, device class, place in the scene), and name the mechanism — jank, shader compile stalls, memory pressure, a render-scale change or a rendering-technology shift. USE FOR: "the app got slower", a FPS drop after a release, stutter reports, deciding whether a regression is real or noise. | `insight_movers`, `insight_anomalies`, `insight_significance`, `insight_baseline`, `perf_summary`, `perf_distribution`, `frame_time_percentiles`, `jank_rate`, `perf_by_device`, `perf_by_scene`, `perf_heatmap`, `compile_stalls`, `resource_percentiles`, `render_scale_truth`, `rendering_technology`, `query` |
|
|
142
|
+
| `weekly_scene_health` | `scene?`, `range?` | A weekly health check for a scene (or the whole project): a weighted health score with every factor traced back to the metric behind it, what changed against last week, traffic, event mix, performance, and the most-interacted meshes. USE FOR: the recurring "how is the scene doing?" review, a scheduled weekly or monthly report, a first look at a project you do not know yet, deciding which scene to investigate next. | `insight_scene_health`, `insight_movers`, `insight_baseline`, `insight_significance`, `insight_anomalies`, `event_counts`, `timeseries`, `perf_summary`, `top_meshes`, `list_sessions`, `query` |
|
|
143
|
+
| `xr_comfort_audit` | `scene?`, `range?` | Audit VR/AR comfort for a scene (or the whole project): rapid head rotation, locomotion style, tracking quality, guardian/boundary contacts, input-source mix, and the short sessions that mean someone took the headset off. USE FOR: motion-sickness complaints, immersive sessions that end early, choosing a locomotion scheme, checking whether a play space is big enough. | `xr_rotation`, `xr_locomotion`, `xr_abandonment`, `xr_sources`, `xr_tracking_quality`, `xr_boundary_contacts`, `boundary_heatmap_stats`, `insight_scene_health`, `insight_movers`, `query` |
|
|
144
|
+
|
|
145
|
+
<!-- generated:registry-skills:end -->
|
|
146
|
+
|
|
112
147
|
## API
|
|
113
148
|
|
|
114
149
|
| Export | Purpose |
|
|
115
150
|
| ------------------------------ | -------------------------------------------------------------------- |
|
|
116
|
-
| `readTools` | The read-only
|
|
151
|
+
| `readTools` | The read-only analytics catalog (one entry per query endpoint). |
|
|
152
|
+
| `writeTools` | The `annotate`-gated project-metadata tools (ADR 0051 §5). |
|
|
117
153
|
| `coreReadTools` | Focused subset for small local models (a filtered view, not a copy). |
|
|
118
154
|
| `selectReadTools(kind)` | Pick `"core"` or `"full"`. |
|
|
119
155
|
| `filterReadTools(names)` | Narrow the catalog to specific tool names, in catalog order. |
|
package/dist/client.d.ts
CHANGED
|
@@ -1,13 +1,26 @@
|
|
|
1
1
|
/** A query-string parameter map. `undefined` values are omitted from the request. */
|
|
2
2
|
export type QueryParams = Record<string, string | number | undefined>;
|
|
3
3
|
/**
|
|
4
|
-
* A minimal
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* A minimal HTTP client over the collector API. It holds no business logic — it
|
|
5
|
+
* is a thin transport that mirrors the dashboard's `CollectorApi` so an agent
|
|
6
|
+
* reads the same aggregated results a human would (ADR 0005 / ADR 0017).
|
|
7
|
+
*
|
|
8
|
+
* `get` is the whole analytics surface: **events are read-only**, and nothing
|
|
9
|
+
* here can write, alter or delete one (ADR 0051 §9). The three write methods
|
|
10
|
+
* exist for exactly one caller — the metadata write tools of #310 (annotations,
|
|
11
|
+
* glossary, saved analyses) — which the MCP server registers only when the key
|
|
12
|
+
* holds `annotate`. They are optional so a hand-built read-only client is still
|
|
13
|
+
* a valid `CollectorClient`; {@link writeTools} reports a clear error instead of
|
|
14
|
+
* calling a method that is not there.
|
|
8
15
|
*/
|
|
9
16
|
export interface CollectorClient {
|
|
10
17
|
get(path: string, params?: QueryParams): Promise<unknown>;
|
|
18
|
+
/** Create a metadata row. Used only by the `annotate` write tools. */
|
|
19
|
+
post?(path: string, body: unknown): Promise<unknown>;
|
|
20
|
+
/** Upsert a metadata row (the glossary). Used only by the `annotate` write tools. */
|
|
21
|
+
put?(path: string, body: unknown): Promise<unknown>;
|
|
22
|
+
/** Delete a metadata row. Used only by the `annotate` write tools. */
|
|
23
|
+
delete?(path: string): Promise<unknown>;
|
|
11
24
|
}
|
|
12
25
|
/**
|
|
13
26
|
* The `(collectorUrl, apiKey)` pair a collector client binds to. Every consumer
|
|
@@ -26,8 +39,12 @@ export declare class CollectorError extends Error {
|
|
|
26
39
|
constructor(message: string, status: number);
|
|
27
40
|
}
|
|
28
41
|
/**
|
|
29
|
-
* Build a
|
|
42
|
+
* Build a collector client bound to one `(collectorUrl, apiKey)` pair.
|
|
30
43
|
* `fetchImpl` is injectable for testing; it defaults to the global `fetch`.
|
|
44
|
+
*
|
|
45
|
+
* The write methods are the narrow metadata path of #310 and nothing else:
|
|
46
|
+
* whether they are allowed at all is decided by the collector from the key's
|
|
47
|
+
* capabilities (`403` without `annotate`), not here.
|
|
31
48
|
*/
|
|
32
49
|
export declare function createCollectorClient(config: CollectorClientConfig, fetchImpl?: typeof fetch): CollectorClient;
|
|
33
50
|
//# sourceMappingURL=client.d.ts.map
|
package/dist/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,qFAAqF;AACrF,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC,CAAC;AAEtE
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,qFAAqF;AACrF,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC,CAAC;AAEtE;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,eAAe;IAC9B,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC1D,sEAAsE;IACtE,IAAI,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACrD,qFAAqF;IACrF,GAAG,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACpD,sEAAsE;IACtE,MAAM,CAAC,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACzC;AAED;;;;GAIG;AACH,MAAM,WAAW,qBAAqB;IACpC,wFAAwF;IACxF,YAAY,EAAE,MAAM,CAAC;IACrB,4DAA4D;IAC5D,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,gEAAgE;AAChE,qBAAa,cAAe,SAAQ,KAAK;IAGrC,QAAQ,CAAC,MAAM,EAAE,MAAM;gBADvB,OAAO,EAAE,MAAM,EACN,MAAM,EAAE,MAAM;CAK1B;AAMD;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,qBAAqB,EAC7B,SAAS,GAAE,OAAO,KAAa,GAC9B,eAAe,CA6CjB"}
|
package/dist/client.js
CHANGED
|
@@ -11,27 +11,56 @@ function ensureTrailingSlash(url) {
|
|
|
11
11
|
return url.endsWith("/") ? url : `${url}/`;
|
|
12
12
|
}
|
|
13
13
|
/**
|
|
14
|
-
* Build a
|
|
14
|
+
* Build a collector client bound to one `(collectorUrl, apiKey)` pair.
|
|
15
15
|
* `fetchImpl` is injectable for testing; it defaults to the global `fetch`.
|
|
16
|
+
*
|
|
17
|
+
* The write methods are the narrow metadata path of #310 and nothing else:
|
|
18
|
+
* whether they are allowed at all is decided by the collector from the key's
|
|
19
|
+
* capabilities (`403` without `annotate`), not here.
|
|
16
20
|
*/
|
|
17
21
|
export function createCollectorClient(config, fetchImpl = fetch) {
|
|
18
22
|
const base = ensureTrailingSlash(config.collectorUrl);
|
|
23
|
+
const resolve = (path) => new URL(path.replace(/^\//, ""), base);
|
|
24
|
+
/** Send a request and turn a non-2xx response into a {@link CollectorError}. */
|
|
25
|
+
async function send(url, init) {
|
|
26
|
+
const res = await fetchImpl(url, {
|
|
27
|
+
...init,
|
|
28
|
+
headers: { "x-api-key": config.apiKey, ...init.headers },
|
|
29
|
+
});
|
|
30
|
+
if (!res.ok) {
|
|
31
|
+
const body = await res.text().catch(() => "");
|
|
32
|
+
throw new CollectorError(body || res.statusText, res.status);
|
|
33
|
+
}
|
|
34
|
+
// A successful delete answers `204 No Content`, which has no body to parse.
|
|
35
|
+
if (res.status === 204)
|
|
36
|
+
return null;
|
|
37
|
+
return res.json();
|
|
38
|
+
}
|
|
19
39
|
return {
|
|
20
40
|
async get(path, params = {}) {
|
|
21
|
-
const url =
|
|
41
|
+
const url = resolve(path);
|
|
22
42
|
for (const [key, value] of Object.entries(params)) {
|
|
23
43
|
if (value != null)
|
|
24
44
|
url.searchParams.set(key, String(value));
|
|
25
45
|
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
46
|
+
return send(url, { method: "GET" });
|
|
47
|
+
},
|
|
48
|
+
async post(path, body) {
|
|
49
|
+
return send(resolve(path), {
|
|
50
|
+
method: "POST",
|
|
51
|
+
headers: { "content-type": "application/json" },
|
|
52
|
+
body: JSON.stringify(body),
|
|
29
53
|
});
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
54
|
+
},
|
|
55
|
+
async put(path, body) {
|
|
56
|
+
return send(resolve(path), {
|
|
57
|
+
method: "PUT",
|
|
58
|
+
headers: { "content-type": "application/json" },
|
|
59
|
+
body: JSON.stringify(body),
|
|
60
|
+
});
|
|
61
|
+
},
|
|
62
|
+
async delete(path) {
|
|
63
|
+
return send(resolve(path), { method: "DELETE" });
|
|
35
64
|
},
|
|
36
65
|
};
|
|
37
66
|
}
|
package/dist/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|