Skip to content

Commit ba4904f

Browse files
authored
feat(telemetry): capture installs from published packages
feat(telemetry): capture installs from published packages
1 parent 1c08e1f commit ba4904f

29 files changed

Lines changed: 463 additions & 211 deletions

apps/website/public/AGENTS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Angular Agent Framework v0.0.28
1+
# Angular Agent Framework v0.0.29
22

33
Agent UI primitives and LangGraph streaming adapters for Angular.
44

apps/website/public/CLAUDE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Angular Agent Framework v0.0.28
1+
# Angular Agent Framework v0.0.29
22

33
Agent UI primitives and LangGraph streaming adapters for Angular.
44

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import { PostHog } from 'posthog-node';
2+
import { NextRequest, NextResponse } from 'next/server';
3+
import { normalizePostHogHost, toSafeAnalyticsString } from '../../../lib/analytics/properties';
4+
5+
const PUBLIC_INGEST_KEY = 'phc_public_cacheplane_telemetry';
6+
7+
interface TelemetryIngestPayload {
8+
key?: unknown;
9+
distinctId?: unknown;
10+
event?: unknown;
11+
properties?: unknown;
12+
}
13+
14+
function getPostHogClient(): PostHog | null {
15+
const token = toSafeAnalyticsString(process.env.NEXT_PUBLIC_POSTHOG_TOKEN, 500);
16+
if (!token) return null;
17+
return new PostHog(token, {
18+
host: normalizePostHogHost(process.env.NEXT_PUBLIC_POSTHOG_HOST),
19+
flushAt: 1,
20+
flushInterval: 0,
21+
});
22+
}
23+
24+
function isRecord(value: unknown): value is Record<string, unknown> {
25+
return typeof value === 'object' && value !== null && !Array.isArray(value);
26+
}
27+
28+
function readPayload(value: unknown): {
29+
distinctId: string;
30+
event: string;
31+
properties: Record<string, unknown>;
32+
} | null {
33+
if (!isRecord(value)) return null;
34+
const payload = value as TelemetryIngestPayload;
35+
if (payload.key !== PUBLIC_INGEST_KEY) return null;
36+
37+
const distinctId = toSafeAnalyticsString(payload.distinctId, 200);
38+
const event = toSafeAnalyticsString(payload.event, 100);
39+
if (!distinctId || !event?.startsWith('ngaf:')) return null;
40+
41+
return {
42+
distinctId,
43+
event,
44+
properties: isRecord(payload.properties) ? payload.properties : {},
45+
};
46+
}
47+
48+
export async function POST(req: NextRequest) {
49+
let body: unknown;
50+
try {
51+
body = await req.json();
52+
} catch {
53+
return NextResponse.json({ error: 'Invalid JSON' }, { status: 400 });
54+
}
55+
56+
const payload = readPayload(body);
57+
if (!payload) return NextResponse.json({ error: 'Invalid telemetry payload' }, { status: 400 });
58+
59+
const posthog = getPostHogClient();
60+
if (!posthog) return NextResponse.json({ error: 'Telemetry ingest is not configured' }, { status: 503 });
61+
62+
try {
63+
posthog.capture({
64+
distinctId: payload.distinctId,
65+
event: payload.event,
66+
properties: {
67+
...payload.properties,
68+
$ip: null,
69+
$process_person_profile: false,
70+
},
71+
});
72+
await posthog.shutdown();
73+
return NextResponse.json({ ok: true }, { status: 202 });
74+
} catch (err) {
75+
console.error('[telemetry-ingest] capture failed:', err);
76+
await posthog.shutdown().catch(() => undefined);
77+
return NextResponse.json({ error: 'Telemetry ingest failed' }, { status: 502 });
78+
}
79+
}

apps/website/src/components/landing/FinalCTA.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ export function FinalCTA({
2424
subtext = 'Install the framework, read the docs, and have a streaming chat in your app this afternoon.',
2525
primary = DEFAULT_PRIMARY,
2626
secondary = DEFAULT_SECONDARY,
27-
caption = 'MIT · No signup required · No telemetry',
27+
caption = 'MIT · No signup required · App telemetry off by default',
2828
}: FinalCTAProps = {}) {
2929
return (
3030
<Section surface="tinted" ariaLabelledBy="final-cta-heading">

apps/website/src/components/landing/Promises.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ const PROMISES = [
1818
body: 'Self-host LangGraph + your Angular app. Run it all in your VPC.',
1919
},
2020
{
21-
title: 'No telemetry',
22-
body: 'We don’t collect anything from your app. Not at install, not at runtime.',
21+
title: 'No app telemetry',
22+
body: 'We don’t collect prompts, completions, tool data, or app runtime content by default. Package installs send a minimal opt-out ping.',
2323
},
2424
{
2525
title: 'No model lock-in',

docs/RELEASE.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ npx nx release patch
1515

1616
This runs Nx Release in interactive mode, which:
1717

18-
1. Builds all seven publishable projects (preVersionCommand).
18+
1. Builds all seven publishable projects and patches install telemetry into the publishable package manifests (preVersionCommand).
1919
2. Bumps every package.json version (e.g., `0.0.1``0.0.2`).
2020
3. Generates `CHANGELOG.md` from commits since the last tag.
2121
4. Creates a git commit `chore(release): publish v0.0.2`.
@@ -50,8 +50,9 @@ npx nx release publish --groups=publishable
5050
The very first publish ships the version currently on disk (`0.0.1`) — no version bump. `--first-release` skips the "previous tag exists" check and the "package already on registry" check.
5151

5252
```bash
53-
# 1. Build everything
53+
# 1. Build everything and patch install telemetry into publishable manifests
5454
npx nx run-many -t build --projects=chat,langgraph,ag-ui,render,a2ui,licensing,telemetry
55+
node libs/telemetry/scripts/apply-install-telemetry.mjs dist/libs/chat dist/libs/langgraph dist/libs/ag-ui dist/libs/render dist/libs/a2ui dist/libs/licensing
5556

5657
# 2. Generate the initial CHANGELOG, commit, and tag v0.0.1
5758
npx nx release changelog 0.0.1 --first-release

docs/gtm/messaging.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,13 @@
1010

1111
**H1:** Ship production agent UIs in Angular.
1212

13-
**Subhead:** Signal-native chat, threads, interrupts, tool progress, and generative UI for LangGraph, AG-UI, and A2UI. MIT-licensed, self-hostable, opt-in telemetry, no React rewrite.
13+
**Subhead:** Signal-native chat, threads, interrupts, tool progress, and generative UI for LangGraph, AG-UI, and A2UI. MIT-licensed, self-hostable, app telemetry off by default, no React rewrite.
1414

1515
**Primary CTA:** `Install @ngaf/chat` (copy-to-clipboard, fires `marketing:cta_click` with `cta_id=hero_install`, `track=developer`).
1616

1717
**Secondary CTA:** `Talk to our engineers` (routes to `/contact?source=home_hero&track=enterprise`, fires `marketing:cta_click` with `cta_id=hero_talk_to_engineers`, `track=enterprise`).
1818

19-
**Proof row:** `MIT · Angular-native Signals · LangGraph + AG-UI · A2UI-compatible · Self-hostable · Opt-in telemetry`
19+
**Proof row:** `MIT · Angular-native Signals · LangGraph + AG-UI · A2UI-compatible · Self-hostable · App telemetry off by default`
2020

2121
**Subline under proof row:** *Not another backend agent runtime. Keep LangGraph, Genkit, Mastra, CrewAI, or your own service. Cacheplane solves the Angular UI layer.*
2222

@@ -27,12 +27,12 @@ Repeat across the site, comparison pages, and content.
2727
1. **Angular-native, not React-translated.** Signals, DI, OnPush, standalone components, Angular testing patterns, design-system ownership.
2828
2. **Complete agent UI, not just stream plumbing.** Messages, status, errors, tool progress, interrupts, branching/history, thread persistence, reload, fallbacks, tests.
2929
3. **Generative UI that respects the enterprise design system.** Approved components from your design system; no arbitrary code shipping.
30-
4. **Enterprise OSS posture.** MIT, no end-user telemetry, no required cloud, self-hosting, optional paid support/SLA.
30+
4. **Enterprise OSS posture.** MIT, no app/runtime content telemetry by default, no required cloud, self-hosting, optional paid support/SLA.
3131
5. **Production patterns, not demo candy.** Real auth, real backends, observability, error boundaries, fallback strategies, CI/CD, load/chaos patterns, runbooks.
3232

3333
## Risk-cleanup copy changes (Spec 2)
3434

35-
- "No telemetry" → "**Opt-in telemetry**" with link to `libs/telemetry/README.md`.
35+
- "No telemetry" → "**App telemetry off by default**" with link to `libs/telemetry/README.md` for the minimal opt-out package install ping.
3636
- "All Angular versions" (pricing) → **real compatibility matrix** with supported/experimental/planned/unsupported.
3737
- "A2UI v1" → **"A2UI v0.9-compatible"** until v1 is verified.
3838
- "Angular Agent Framework" → **"Agent UI for Angular"** (category sweep, with care for substring overlap per existing memory note).

docs/gtm/taxonomy.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,8 @@ The standard PostHog `$pageview` event is used as-is across all three surfaces.
6464

6565
| Event | When | Surface | Default |
6666
|--------------------------------------|--------------------------------------------|-----------------|--------------|
67-
| `ngaf:postinstall` | `npm install` of an `@ngaf/*` package | Node (script) | **Opt-out** |
67+
| `ngaf:postinstall` | Dependency/global install of a published `@ngaf/*` package | Node (script) | **Opt-out** |
6868
| `ngaf:runtime_instance_created` | Server adapter init | Node | **Opt-out** |
69-
| `ngaf:runtime_request_created` | Server adapter handles a request | Node | **Opt-out** |
7069
| `ngaf:stream_started` | Stream begins | Node | **Opt-out** |
7170
| `ngaf:stream_ended` | Stream ends normally | Node | **Opt-out** |
7271
| `ngaf:stream_errored` | Stream errors | Node | **Opt-out** |

gtm.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ Operational progress lives in agent runs and PostHog. The repo holds durable str
9292
## 8. Non-goals (current phase)
9393

9494
- We do not compete as a general agent UI framework. We claim the Angular final mile.
95-
- We do not ship telemetry from `@ngaf/*` browser packages by default. Opt-in only. Node-side telemetry honors `DO_NOT_TRACK` and `NGAF_TELEMETRY_DISABLED`; see [libs/telemetry/README.md](libs/telemetry/README.md) for the full contract.
95+
- We do not ship telemetry from `@ngaf/*` browser packages by default. Opt-in only. Node-side package telemetry is minimal and honors `DO_NOT_TRACK`, `npm_config_do_not_track`, and `NGAF_TELEMETRY_DISABLED`; see [libs/telemetry/README.md](libs/telemetry/README.md) for the full contract.
9696
- We do not run paid acquisition until Phase 2 organic baselines exist.
9797
- We do not pursue stars as a vanity metric.
9898
- We do not run A/B positioning experiments in Phase 1. Ship one hero, measure, iterate.

libs/ag-ui/project.json

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,6 @@
44
"sourceRoot": "libs/ag-ui/src",
55
"prefix": "lib",
66
"projectType": "library",
7-
"release": {
8-
"version": {
9-
"manifestRootsToUpdate": [
10-
"{projectRoot}"
11-
],
12-
"currentVersionResolver": "git-tag",
13-
"fallbackCurrentVersionResolver": "disk"
14-
}
15-
},
167
"tags": [],
178
"targets": {
189
"build": {

0 commit comments

Comments
 (0)