umami is now a whole module, not a manifest: its own API client, its
tools, and its provisioner all live in the module and build on
@novox/mesh-sdk.
- client.ts — umami's API client, moved out of the shared sdk into the
module (ADR 0044); umami's tools and provisioner both import it.
- tools/ — umami_create_site / umami_delete_site on the sdk tool harness
(registerModuleTools); the tool logic and client are the module's.
- provisioner/ — the adapter making umami a provider of the mesh
'analytics' interface: a consumer contributes {domain}, receives
{siteId, snippet, dashboard}. ~20 lines, because the watch/seal/grant
loop is the sdk harness's.
Type-checks against the real mesh-sdk (tsc --noEmit clean); the manifest
parses against internal/catalogue. Remaining to actually run: build and
publish the module + its mesh-provision-umami-analytics image (the
placeholder digest), which is the pipeline's job.
Claude-Session: https://claude.ai/code/session_01LrgweAeERJYBg88c5cKDzF
36 lines
1.4 KiB
TypeScript
36 lines
1.4 KiB
TypeScript
// umami's provisioner — the adapter that makes umami a provider of the mesh `analytics` interface.
|
|
// The watching, sealing and grant-file handling are the sdk harness's; this writes only the
|
|
// per-service half: how umami creates and removes a tracked site (novox/hq ADR 0044/0045).
|
|
//
|
|
// The `analytics` interface: a consumer contributes `{ domain }` (the site it wants tracked) and
|
|
// receives `{ siteId, snippet, dashboard }`. umami adapts its own API to that contract, so a
|
|
// consumer depends on `analytics`, not on umami.
|
|
|
|
import { runProvisioner, type Grant, type Credential } from "@novox/mesh-sdk/provisioner";
|
|
import { UmamiClient } from "../client.js";
|
|
|
|
const umami = UmamiClient.fromEnv();
|
|
|
|
runProvisioner("analytics", {
|
|
async create(grant: Grant): Promise<Credential> {
|
|
const domain = grant.values.domain ?? grant.consumer;
|
|
const name = grant.values.name ?? domain;
|
|
const token = await umami.getToken();
|
|
const site = (await umami.findWebsite(token, domain)) ?? (await umami.createWebsite(token, domain, name));
|
|
return {
|
|
fields: {
|
|
siteId: site.id,
|
|
snippet: umami.snippet(site.id),
|
|
dashboard: umami.dashboard(site.id),
|
|
},
|
|
};
|
|
},
|
|
|
|
async remove(grant: Grant): Promise<void> {
|
|
const domain = grant.values.domain ?? grant.consumer;
|
|
const token = await umami.getToken();
|
|
const site = await umami.findWebsite(token, domain);
|
|
if (site) await umami.deleteWebsite(token, site.id);
|
|
},
|
|
});
|