@threadplane/telemetry 0.0.53 → 0.0.54
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/README.md +16 -16
- package/browser/README.md +16 -16
- package/browser/fesm2022/threadplane-telemetry.mjs +5 -5
- package/browser/fesm2022/threadplane-telemetry.mjs.map +1 -1
- package/browser/types/threadplane-telemetry.d.ts +1 -1
- package/node/adapter.js +5 -5
- package/node/client.js +3 -3
- package/node/postinstall.js +4 -4
- package/package.json +1 -1
- package/shared/env.d.ts +1 -1
- package/shared/env.d.ts.map +1 -1
- package/shared/env.js +2 -2
- package/shared/events.d.ts +2 -2
- package/shared/events.d.ts.map +1 -1
package/README.md
CHANGED
|
@@ -28,12 +28,12 @@ Transparent, opt-out anonymous usage telemetry for the Threadplane framework. Is
|
|
|
28
28
|
|
|
29
29
|
| Event | What is sent |
|
|
30
30
|
|-------|-------------|
|
|
31
|
-
| `
|
|
32
|
-
| `
|
|
33
|
-
| `
|
|
34
|
-
| `
|
|
35
|
-
| `
|
|
36
|
-
| `
|
|
31
|
+
| `tplane:postinstall` | Package name, package version, Node version, OS, CPU architecture, package manager name/version, workspace/global install flags when npm exposes them, sample weight. Per-process anonymous id. No project path, no raw environment variables, no dependency tree, no installer IP address. |
|
|
32
|
+
| `tplane:runtime_instance_created` | Which transport, which model provider (string), Angular peer version. No API keys, no endpoint hostnames, no user data. |
|
|
33
|
+
| `tplane:runtime_request_created` | Transport, request type, provider, model. No prompts, thread IDs, assistant IDs, endpoint URLs, or headers. |
|
|
34
|
+
| `tplane:stream_started` | Provider, model name. No prompts, no message content. |
|
|
35
|
+
| `tplane:stream_ended` | Provider, model name, duration. No prompts, no completions, no message content. |
|
|
36
|
+
| `tplane:stream_errored` | Provider, model name, error class. No prompts, no completions, no message content. |
|
|
37
37
|
|
|
38
38
|
### Browser events (opt-in, off by default)
|
|
39
39
|
|
|
@@ -41,10 +41,10 @@ Nothing fires unless `provideThreadplaneTelemetry({ enabled: true, ... })` is ca
|
|
|
41
41
|
|
|
42
42
|
| Event | What is sent |
|
|
43
43
|
|-------|-------------|
|
|
44
|
-
| `
|
|
45
|
-
| `
|
|
44
|
+
| `tplane:browser_provided` | Telemetry initialized; surface name, sample weight. Anonymous, no user data. |
|
|
45
|
+
| `tplane:browser_chat_init` | Chat component initialized; surface name, sample weight. Anonymous, no message content. |
|
|
46
46
|
|
|
47
|
-
Browser-side runtime lifecycle events (`
|
|
47
|
+
Browser-side runtime lifecycle events (`tplane:runtime_instance_created`, `tplane:runtime_request_created`, `tplane:stream_started`, `tplane:stream_ended`, `tplane:stream_errored`) may also be sent when the app captures them explicitly. Same payload constraints as Node.
|
|
48
48
|
|
|
49
49
|
### Never collected — by anyone, at any time
|
|
50
50
|
|
|
@@ -73,7 +73,7 @@ Node telemetry is on by default. Any one of the following turns it off entirely.
|
|
|
73
73
|
|
|
74
74
|
| Variable | Value | Notes |
|
|
75
75
|
|----------|-------|-------|
|
|
76
|
-
| `
|
|
76
|
+
| `TPLANE_TELEMETRY_DISABLED` | `1` or `true` | Package-level kill-switch |
|
|
77
77
|
| `DO_NOT_TRACK` | `1` or `true` | Cross-vendor standard; `npm_config_do_not_track` and `NPM_CONFIG_DO_NOT_TRACK` are also respected |
|
|
78
78
|
|
|
79
79
|
### CI auto-disable
|
|
@@ -98,7 +98,7 @@ disableTelemetry();
|
|
|
98
98
|
Default sample rate is **1.0** (100%). Reduce it via:
|
|
99
99
|
|
|
100
100
|
```bash
|
|
101
|
-
|
|
101
|
+
TPLANE_TELEMETRY_SAMPLE_RATE=0.1 # sample 10% of events
|
|
102
102
|
```
|
|
103
103
|
|
|
104
104
|
Every event carries a `sample_weight` property so de-sampling at query time works correctly.
|
|
@@ -108,10 +108,10 @@ Every event carries a `sample_weight` property so de-sampling at query time work
|
|
|
108
108
|
Redirect all Node telemetry to your own endpoint:
|
|
109
109
|
|
|
110
110
|
```bash
|
|
111
|
-
|
|
111
|
+
TPLANE_TELEMETRY_INGEST_URL=https://telemetry.acme-internal.example.com/api/ingest
|
|
112
112
|
```
|
|
113
113
|
|
|
114
|
-
The default ingest (when unset) is a thin proxy at `https://threadplane.ai/api/ingest` that accepts the `@threadplane/telemetry` JSON payload, forwards `
|
|
114
|
+
The default ingest (when unset) is a thin proxy at `https://threadplane.ai/api/ingest` that accepts the `@threadplane/telemetry` JSON payload, forwards `tplane:*` events to the project PostHog instance, and does not forward installer IP addresses. Source lives in `apps/website/src/app/api/ingest/`.
|
|
115
115
|
|
|
116
116
|
## Usage
|
|
117
117
|
|
|
@@ -175,7 +175,7 @@ import {
|
|
|
175
175
|
} from '@threadplane/telemetry';
|
|
176
176
|
```
|
|
177
177
|
|
|
178
|
-
`getDisableReason()` returns `'DO_NOT_TRACK' | '
|
|
178
|
+
`getDisableReason()` returns `'DO_NOT_TRACK' | 'TPLANE_TELEMETRY_DISABLED' | 'CI' | null`.
|
|
179
179
|
|
|
180
180
|
## Reliability and transparency
|
|
181
181
|
|
|
@@ -184,10 +184,10 @@ import {
|
|
|
184
184
|
Inspect payloads locally without sending them:
|
|
185
185
|
|
|
186
186
|
```bash
|
|
187
|
-
DEBUG=
|
|
187
|
+
DEBUG=tplane:telemetry npm install
|
|
188
188
|
```
|
|
189
189
|
|
|
190
|
-
`
|
|
190
|
+
`tplane:telemetry` is the debug namespace — it is not an event name and is never sent to the ingest endpoint.
|
|
191
191
|
|
|
192
192
|
### Anonymous id strategy
|
|
193
193
|
|
package/browser/README.md
CHANGED
|
@@ -28,12 +28,12 @@ Transparent, opt-out anonymous usage telemetry for the Threadplane framework. Is
|
|
|
28
28
|
|
|
29
29
|
| Event | What is sent |
|
|
30
30
|
|-------|-------------|
|
|
31
|
-
| `
|
|
32
|
-
| `
|
|
33
|
-
| `
|
|
34
|
-
| `
|
|
35
|
-
| `
|
|
36
|
-
| `
|
|
31
|
+
| `tplane:postinstall` | Package name, package version, Node version, OS, CPU architecture, package manager name/version, workspace/global install flags when npm exposes them, sample weight. Per-process anonymous id. No project path, no raw environment variables, no dependency tree, no installer IP address. |
|
|
32
|
+
| `tplane:runtime_instance_created` | Which transport, which model provider (string), Angular peer version. No API keys, no endpoint hostnames, no user data. |
|
|
33
|
+
| `tplane:runtime_request_created` | Transport, request type, provider, model. No prompts, thread IDs, assistant IDs, endpoint URLs, or headers. |
|
|
34
|
+
| `tplane:stream_started` | Provider, model name. No prompts, no message content. |
|
|
35
|
+
| `tplane:stream_ended` | Provider, model name, duration. No prompts, no completions, no message content. |
|
|
36
|
+
| `tplane:stream_errored` | Provider, model name, error class. No prompts, no completions, no message content. |
|
|
37
37
|
|
|
38
38
|
### Browser events (opt-in, off by default)
|
|
39
39
|
|
|
@@ -41,10 +41,10 @@ Nothing fires unless `provideThreadplaneTelemetry({ enabled: true, ... })` is ca
|
|
|
41
41
|
|
|
42
42
|
| Event | What is sent |
|
|
43
43
|
|-------|-------------|
|
|
44
|
-
| `
|
|
45
|
-
| `
|
|
44
|
+
| `tplane:browser_provided` | Telemetry initialized; surface name, sample weight. Anonymous, no user data. |
|
|
45
|
+
| `tplane:browser_chat_init` | Chat component initialized; surface name, sample weight. Anonymous, no message content. |
|
|
46
46
|
|
|
47
|
-
Browser-side runtime lifecycle events (`
|
|
47
|
+
Browser-side runtime lifecycle events (`tplane:runtime_instance_created`, `tplane:runtime_request_created`, `tplane:stream_started`, `tplane:stream_ended`, `tplane:stream_errored`) may also be sent when the app captures them explicitly. Same payload constraints as Node.
|
|
48
48
|
|
|
49
49
|
### Never collected — by anyone, at any time
|
|
50
50
|
|
|
@@ -73,7 +73,7 @@ Node telemetry is on by default. Any one of the following turns it off entirely.
|
|
|
73
73
|
|
|
74
74
|
| Variable | Value | Notes |
|
|
75
75
|
|----------|-------|-------|
|
|
76
|
-
| `
|
|
76
|
+
| `TPLANE_TELEMETRY_DISABLED` | `1` or `true` | Package-level kill-switch |
|
|
77
77
|
| `DO_NOT_TRACK` | `1` or `true` | Cross-vendor standard; `npm_config_do_not_track` and `NPM_CONFIG_DO_NOT_TRACK` are also respected |
|
|
78
78
|
|
|
79
79
|
### CI auto-disable
|
|
@@ -98,7 +98,7 @@ disableTelemetry();
|
|
|
98
98
|
Default sample rate is **1.0** (100%). Reduce it via:
|
|
99
99
|
|
|
100
100
|
```bash
|
|
101
|
-
|
|
101
|
+
TPLANE_TELEMETRY_SAMPLE_RATE=0.1 # sample 10% of events
|
|
102
102
|
```
|
|
103
103
|
|
|
104
104
|
Every event carries a `sample_weight` property so de-sampling at query time works correctly.
|
|
@@ -108,10 +108,10 @@ Every event carries a `sample_weight` property so de-sampling at query time work
|
|
|
108
108
|
Redirect all Node telemetry to your own endpoint:
|
|
109
109
|
|
|
110
110
|
```bash
|
|
111
|
-
|
|
111
|
+
TPLANE_TELEMETRY_INGEST_URL=https://telemetry.acme-internal.example.com/api/ingest
|
|
112
112
|
```
|
|
113
113
|
|
|
114
|
-
The default ingest (when unset) is a thin proxy at `https://threadplane.ai/api/ingest` that accepts the `@threadplane/telemetry` JSON payload, forwards `
|
|
114
|
+
The default ingest (when unset) is a thin proxy at `https://threadplane.ai/api/ingest` that accepts the `@threadplane/telemetry` JSON payload, forwards `tplane:*` events to the project PostHog instance, and does not forward installer IP addresses. Source lives in `apps/website/src/app/api/ingest/`.
|
|
115
115
|
|
|
116
116
|
## Usage
|
|
117
117
|
|
|
@@ -175,7 +175,7 @@ import {
|
|
|
175
175
|
} from '@threadplane/telemetry';
|
|
176
176
|
```
|
|
177
177
|
|
|
178
|
-
`getDisableReason()` returns `'DO_NOT_TRACK' | '
|
|
178
|
+
`getDisableReason()` returns `'DO_NOT_TRACK' | 'TPLANE_TELEMETRY_DISABLED' | 'CI' | null`.
|
|
179
179
|
|
|
180
180
|
## Reliability and transparency
|
|
181
181
|
|
|
@@ -184,10 +184,10 @@ import {
|
|
|
184
184
|
Inspect payloads locally without sending them:
|
|
185
185
|
|
|
186
186
|
```bash
|
|
187
|
-
DEBUG=
|
|
187
|
+
DEBUG=tplane:telemetry npm install
|
|
188
188
|
```
|
|
189
189
|
|
|
190
|
-
`
|
|
190
|
+
`tplane:telemetry` is the debug namespace — it is not an event name and is never sent to the ingest endpoint.
|
|
191
191
|
|
|
192
192
|
### Anonymous id strategy
|
|
193
193
|
|
|
@@ -59,20 +59,20 @@ class ThreadplaneTelemetryService {
|
|
|
59
59
|
}
|
|
60
60
|
}
|
|
61
61
|
captureRuntimeInstanceCreated(input) {
|
|
62
|
-
return this.capture('
|
|
62
|
+
return this.capture('tplane:runtime_instance_created', { ...input });
|
|
63
63
|
}
|
|
64
64
|
captureRuntimeRequestCreated(input) {
|
|
65
|
-
return this.capture('
|
|
65
|
+
return this.capture('tplane:runtime_request_created', { ...input });
|
|
66
66
|
}
|
|
67
67
|
captureStreamStarted(input) {
|
|
68
|
-
return this.capture('
|
|
68
|
+
return this.capture('tplane:stream_started', { ...input });
|
|
69
69
|
}
|
|
70
70
|
captureStreamEnded(input) {
|
|
71
|
-
return this.capture('
|
|
71
|
+
return this.capture('tplane:stream_ended', { ...input });
|
|
72
72
|
}
|
|
73
73
|
captureStreamErrored(input) {
|
|
74
74
|
const { error, ...rest } = input;
|
|
75
|
-
return this.capture('
|
|
75
|
+
return this.capture('tplane:stream_errored', {
|
|
76
76
|
...rest,
|
|
77
77
|
errorClass: errorClass(error),
|
|
78
78
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"threadplane-telemetry.mjs","sources":["../../../../../libs/telemetry/src/browser/tokens.ts","../../../../../libs/telemetry/src/browser/service.ts","../../../../../libs/telemetry/src/browser/provide.ts","../../../../../libs/telemetry/src/browser/properties.ts","../../../../../libs/telemetry/src/browser/threadplane-telemetry.ts"],"sourcesContent":["import { InjectionToken } from '@angular/core';\n\nexport type ThreadplaneTelemetryEvent =\n | 'ngaf:browser_provided'\n | 'ngaf:browser_chat_init'\n | 'ngaf:runtime_instance_created'\n | 'ngaf:runtime_request_created'\n | 'ngaf:stream_started'\n | 'ngaf:stream_ended'\n | 'ngaf:stream_errored';\n\nexport interface ThreadplaneTelemetryEventPayload {\n event: ThreadplaneTelemetryEvent;\n properties?: Record<string, unknown>;\n}\n\nexport type ThreadplaneTelemetrySink = (payload: ThreadplaneTelemetryEventPayload) => void | Promise<void>;\n\nexport interface ThreadplaneTelemetryConfig {\n enabled: boolean;\n /**\n * Preferred app-owned delivery hook. Use this when the consuming app wants\n * to forward events through its own analytics boundary.\n */\n sink?: ThreadplaneTelemetrySink;\n /**\n * Preferred app-owned ingest URL. The browser service POSTs neutral event\n * payloads here; the endpoint decides where they ultimately go.\n */\n endpoint?: string;\n /** @deprecated Prefer sink or endpoint so public app code is vendor-neutral. */\n posthogKey?: string;\n /** @deprecated Prefer sink or endpoint so public app code is vendor-neutral. */\n posthogHost?: string;\n sampleRate?: number;\n}\n\nexport const THREADPLANE_TELEMETRY_CONFIG = new InjectionToken<ThreadplaneTelemetryConfig | null>(\n 'THREADPLANE_TELEMETRY_CONFIG',\n);\n","import { Injectable, inject } from '@angular/core';\nimport {\n THREADPLANE_TELEMETRY_CONFIG,\n type ThreadplaneTelemetryConfig,\n type ThreadplaneTelemetryEvent,\n} from './tokens';\n\n// Inlined from shared/events.ts: ng-packagr enforces rootDir at the entry-file\n// level (src/browser/), so the browser entry cannot import from ../shared/.\n// Keep this type in sync with shared/events.ts.\nexport type ThreadplaneBrowserEvent = ThreadplaneTelemetryEvent;\n\nexport interface ThreadplaneBrowserRuntimeTelemetry {\n transport: string;\n surface?: string;\n provider?: string;\n model?: string;\n}\n\nexport interface ThreadplaneBrowserStreamTelemetry extends ThreadplaneBrowserRuntimeTelemetry {\n durationMs?: number;\n}\n\nexport interface ThreadplaneBrowserStreamErrorTelemetry extends ThreadplaneBrowserStreamTelemetry {\n error?: unknown;\n}\n\nfunction normalizeSampleRate(sampleRate: number | undefined): number {\n if (sampleRate === undefined) return 1;\n if (!Number.isFinite(sampleRate)) return 1;\n if (sampleRate <= 0) return 0;\n if (sampleRate >= 1) return 1;\n return sampleRate;\n}\n\nfunction errorClass(error: unknown): string {\n if (error instanceof Error && error.name) return error.name;\n if (error && typeof error === 'object' && 'name' in error && typeof error.name === 'string') {\n return error.name;\n }\n return 'UnknownError';\n}\n\n@Injectable({ providedIn: 'root' })\nexport class ThreadplaneTelemetryService {\n private config: ThreadplaneTelemetryConfig | null = inject(THREADPLANE_TELEMETRY_CONFIG, { optional: true });\n private postHogPromise: Promise<typeof import('posthog-js')['default'] | null> | null = null;\n private distinctId: string | null = null;\n\n async capture(event: ThreadplaneTelemetryEvent, properties?: Record<string, unknown>): Promise<void> {\n if (!this.config?.enabled) return;\n const sampleRate = normalizeSampleRate(this.config.sampleRate);\n if (sampleRate === 0) return;\n if (sampleRate < 1 && Math.random() >= sampleRate) return;\n\n const enrichedProperties = {\n ...(properties ?? {}),\n sample_weight: properties?.['sample_weight'] ?? 1 / sampleRate,\n };\n\n try {\n if (this.config.sink) {\n await this.config.sink({ event, properties: enrichedProperties });\n return;\n }\n if (this.config.endpoint) {\n await this.captureEndpoint(event, enrichedProperties);\n return;\n }\n if (!this.config.posthogKey) return;\n const ph = await this.loadPostHog();\n if (!ph) return;\n ph.capture(event, enrichedProperties);\n } catch {\n // silent fail\n }\n }\n\n captureRuntimeInstanceCreated(input: ThreadplaneBrowserRuntimeTelemetry): Promise<void> {\n return this.capture('ngaf:runtime_instance_created', { ...input });\n }\n\n captureRuntimeRequestCreated(input: ThreadplaneBrowserRuntimeTelemetry & { requestType: string }): Promise<void> {\n return this.capture('ngaf:runtime_request_created', { ...input });\n }\n\n captureStreamStarted(input: ThreadplaneBrowserStreamTelemetry): Promise<void> {\n return this.capture('ngaf:stream_started', { ...input });\n }\n\n captureStreamEnded(input: ThreadplaneBrowserStreamTelemetry): Promise<void> {\n return this.capture('ngaf:stream_ended', { ...input });\n }\n\n captureStreamErrored(input: ThreadplaneBrowserStreamErrorTelemetry): Promise<void> {\n const { error, ...rest } = input;\n return this.capture('ngaf:stream_errored', {\n ...rest,\n errorClass: errorClass(error),\n });\n }\n\n private async captureEndpoint(event: ThreadplaneTelemetryEvent, properties: Record<string, unknown>): Promise<void> {\n if (typeof fetch !== 'function' || !this.config?.endpoint) return;\n await fetch(this.config.endpoint, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n keepalive: true,\n body: JSON.stringify({\n event,\n distinctId: this.getDistinctId(),\n properties,\n }),\n });\n }\n\n private getDistinctId(): string {\n if (!this.distinctId) {\n const cryptoApi = globalThis.crypto as Crypto | undefined;\n const value = typeof cryptoApi?.randomUUID === 'function'\n ? cryptoApi.randomUUID()\n : Math.random().toString(36).slice(2, 12);\n this.distinctId = `browser:${value}`;\n }\n return this.distinctId;\n }\n\n private loadPostHog(): Promise<typeof import('posthog-js')['default'] | null> {\n if (!this.postHogPromise) {\n this.postHogPromise = import('posthog-js').then((mod) => {\n if (!this.config?.posthogKey) return null;\n mod.default.init(this.config.posthogKey, {\n api_host: this.config.posthogHost ?? 'https://us.i.posthog.com',\n });\n return mod.default;\n }).catch(() => null);\n }\n return this.postHogPromise;\n }\n}\n","import { makeEnvironmentProviders, type EnvironmentProviders } from '@angular/core';\nimport { THREADPLANE_TELEMETRY_CONFIG, type ThreadplaneTelemetryConfig } from './tokens';\nimport { ThreadplaneTelemetryService } from './service';\n\nexport function provideThreadplaneTelemetry(config: ThreadplaneTelemetryConfig): EnvironmentProviders {\n return makeEnvironmentProviders([\n { provide: THREADPLANE_TELEMETRY_CONFIG, useValue: config },\n ThreadplaneTelemetryService,\n ]);\n}\n","// SPDX-License-Identifier: MIT\n// Note: kept self-contained (no `../shared` import) so ng-packagr can compile\n// this entry point in isolation. The trim+truncate hardening is duplicated\n// from shared/properties.ts on purpose — it's a few lines, and the alternative\n// is a secondary ng-packagr entry point for shared, which is heavier.\nfunction toSafeString(value: unknown, maxLength: number): string | undefined {\n if (typeof value !== 'string') return undefined;\n const trimmed = value.trim();\n if (!trimmed) return undefined;\n return trimmed.slice(0, maxLength);\n}\n\nexport type CaptureConfig = {\n token?: string;\n captureLocal?: boolean;\n host?: string;\n};\n\nexport function isLocalAnalyticsHost(host: unknown): boolean {\n const value = toSafeString(host, 300)?.toLowerCase();\n if (!value) return false;\n\n // IPv6 literal `::1` or `[::1]:port` — don't naively split on `:`.\n if (value === '::1' || value.startsWith('[::1]')) return true;\n\n const hostname = value.split(':')[0];\n return hostname === 'localhost' || hostname === '127.0.0.1';\n}\n\nexport function shouldCaptureAnalytics({ token, captureLocal = false, host }: CaptureConfig): boolean {\n if (!toSafeString(token, 500)) return false;\n if (isLocalAnalyticsHost(host) && !captureLocal) return false;\n return true;\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"threadplane-telemetry.mjs","sources":["../../../../../libs/telemetry/src/browser/tokens.ts","../../../../../libs/telemetry/src/browser/service.ts","../../../../../libs/telemetry/src/browser/provide.ts","../../../../../libs/telemetry/src/browser/properties.ts","../../../../../libs/telemetry/src/browser/threadplane-telemetry.ts"],"sourcesContent":["import { InjectionToken } from '@angular/core';\n\nexport type ThreadplaneTelemetryEvent =\n | 'tplane:browser_provided'\n | 'tplane:browser_chat_init'\n | 'tplane:runtime_instance_created'\n | 'tplane:runtime_request_created'\n | 'tplane:stream_started'\n | 'tplane:stream_ended'\n | 'tplane:stream_errored';\n\nexport interface ThreadplaneTelemetryEventPayload {\n event: ThreadplaneTelemetryEvent;\n properties?: Record<string, unknown>;\n}\n\nexport type ThreadplaneTelemetrySink = (payload: ThreadplaneTelemetryEventPayload) => void | Promise<void>;\n\nexport interface ThreadplaneTelemetryConfig {\n enabled: boolean;\n /**\n * Preferred app-owned delivery hook. Use this when the consuming app wants\n * to forward events through its own analytics boundary.\n */\n sink?: ThreadplaneTelemetrySink;\n /**\n * Preferred app-owned ingest URL. The browser service POSTs neutral event\n * payloads here; the endpoint decides where they ultimately go.\n */\n endpoint?: string;\n /** @deprecated Prefer sink or endpoint so public app code is vendor-neutral. */\n posthogKey?: string;\n /** @deprecated Prefer sink or endpoint so public app code is vendor-neutral. */\n posthogHost?: string;\n sampleRate?: number;\n}\n\nexport const THREADPLANE_TELEMETRY_CONFIG = new InjectionToken<ThreadplaneTelemetryConfig | null>(\n 'THREADPLANE_TELEMETRY_CONFIG',\n);\n","import { Injectable, inject } from '@angular/core';\nimport {\n THREADPLANE_TELEMETRY_CONFIG,\n type ThreadplaneTelemetryConfig,\n type ThreadplaneTelemetryEvent,\n} from './tokens';\n\n// Inlined from shared/events.ts: ng-packagr enforces rootDir at the entry-file\n// level (src/browser/), so the browser entry cannot import from ../shared/.\n// Keep this type in sync with shared/events.ts.\nexport type ThreadplaneBrowserEvent = ThreadplaneTelemetryEvent;\n\nexport interface ThreadplaneBrowserRuntimeTelemetry {\n transport: string;\n surface?: string;\n provider?: string;\n model?: string;\n}\n\nexport interface ThreadplaneBrowserStreamTelemetry extends ThreadplaneBrowserRuntimeTelemetry {\n durationMs?: number;\n}\n\nexport interface ThreadplaneBrowserStreamErrorTelemetry extends ThreadplaneBrowserStreamTelemetry {\n error?: unknown;\n}\n\nfunction normalizeSampleRate(sampleRate: number | undefined): number {\n if (sampleRate === undefined) return 1;\n if (!Number.isFinite(sampleRate)) return 1;\n if (sampleRate <= 0) return 0;\n if (sampleRate >= 1) return 1;\n return sampleRate;\n}\n\nfunction errorClass(error: unknown): string {\n if (error instanceof Error && error.name) return error.name;\n if (error && typeof error === 'object' && 'name' in error && typeof error.name === 'string') {\n return error.name;\n }\n return 'UnknownError';\n}\n\n@Injectable({ providedIn: 'root' })\nexport class ThreadplaneTelemetryService {\n private config: ThreadplaneTelemetryConfig | null = inject(THREADPLANE_TELEMETRY_CONFIG, { optional: true });\n private postHogPromise: Promise<typeof import('posthog-js')['default'] | null> | null = null;\n private distinctId: string | null = null;\n\n async capture(event: ThreadplaneTelemetryEvent, properties?: Record<string, unknown>): Promise<void> {\n if (!this.config?.enabled) return;\n const sampleRate = normalizeSampleRate(this.config.sampleRate);\n if (sampleRate === 0) return;\n if (sampleRate < 1 && Math.random() >= sampleRate) return;\n\n const enrichedProperties = {\n ...(properties ?? {}),\n sample_weight: properties?.['sample_weight'] ?? 1 / sampleRate,\n };\n\n try {\n if (this.config.sink) {\n await this.config.sink({ event, properties: enrichedProperties });\n return;\n }\n if (this.config.endpoint) {\n await this.captureEndpoint(event, enrichedProperties);\n return;\n }\n if (!this.config.posthogKey) return;\n const ph = await this.loadPostHog();\n if (!ph) return;\n ph.capture(event, enrichedProperties);\n } catch {\n // silent fail\n }\n }\n\n captureRuntimeInstanceCreated(input: ThreadplaneBrowserRuntimeTelemetry): Promise<void> {\n return this.capture('tplane:runtime_instance_created', { ...input });\n }\n\n captureRuntimeRequestCreated(input: ThreadplaneBrowserRuntimeTelemetry & { requestType: string }): Promise<void> {\n return this.capture('tplane:runtime_request_created', { ...input });\n }\n\n captureStreamStarted(input: ThreadplaneBrowserStreamTelemetry): Promise<void> {\n return this.capture('tplane:stream_started', { ...input });\n }\n\n captureStreamEnded(input: ThreadplaneBrowserStreamTelemetry): Promise<void> {\n return this.capture('tplane:stream_ended', { ...input });\n }\n\n captureStreamErrored(input: ThreadplaneBrowserStreamErrorTelemetry): Promise<void> {\n const { error, ...rest } = input;\n return this.capture('tplane:stream_errored', {\n ...rest,\n errorClass: errorClass(error),\n });\n }\n\n private async captureEndpoint(event: ThreadplaneTelemetryEvent, properties: Record<string, unknown>): Promise<void> {\n if (typeof fetch !== 'function' || !this.config?.endpoint) return;\n await fetch(this.config.endpoint, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n keepalive: true,\n body: JSON.stringify({\n event,\n distinctId: this.getDistinctId(),\n properties,\n }),\n });\n }\n\n private getDistinctId(): string {\n if (!this.distinctId) {\n const cryptoApi = globalThis.crypto as Crypto | undefined;\n const value = typeof cryptoApi?.randomUUID === 'function'\n ? cryptoApi.randomUUID()\n : Math.random().toString(36).slice(2, 12);\n this.distinctId = `browser:${value}`;\n }\n return this.distinctId;\n }\n\n private loadPostHog(): Promise<typeof import('posthog-js')['default'] | null> {\n if (!this.postHogPromise) {\n this.postHogPromise = import('posthog-js').then((mod) => {\n if (!this.config?.posthogKey) return null;\n mod.default.init(this.config.posthogKey, {\n api_host: this.config.posthogHost ?? 'https://us.i.posthog.com',\n });\n return mod.default;\n }).catch(() => null);\n }\n return this.postHogPromise;\n }\n}\n","import { makeEnvironmentProviders, type EnvironmentProviders } from '@angular/core';\nimport { THREADPLANE_TELEMETRY_CONFIG, type ThreadplaneTelemetryConfig } from './tokens';\nimport { ThreadplaneTelemetryService } from './service';\n\nexport function provideThreadplaneTelemetry(config: ThreadplaneTelemetryConfig): EnvironmentProviders {\n return makeEnvironmentProviders([\n { provide: THREADPLANE_TELEMETRY_CONFIG, useValue: config },\n ThreadplaneTelemetryService,\n ]);\n}\n","// SPDX-License-Identifier: MIT\n// Note: kept self-contained (no `../shared` import) so ng-packagr can compile\n// this entry point in isolation. The trim+truncate hardening is duplicated\n// from shared/properties.ts on purpose — it's a few lines, and the alternative\n// is a secondary ng-packagr entry point for shared, which is heavier.\nfunction toSafeString(value: unknown, maxLength: number): string | undefined {\n if (typeof value !== 'string') return undefined;\n const trimmed = value.trim();\n if (!trimmed) return undefined;\n return trimmed.slice(0, maxLength);\n}\n\nexport type CaptureConfig = {\n token?: string;\n captureLocal?: boolean;\n host?: string;\n};\n\nexport function isLocalAnalyticsHost(host: unknown): boolean {\n const value = toSafeString(host, 300)?.toLowerCase();\n if (!value) return false;\n\n // IPv6 literal `::1` or `[::1]:port` — don't naively split on `:`.\n if (value === '::1' || value.startsWith('[::1]')) return true;\n\n const hostname = value.split(':')[0];\n return hostname === 'localhost' || hostname === '127.0.0.1';\n}\n\nexport function shouldCaptureAnalytics({ token, captureLocal = false, host }: CaptureConfig): boolean {\n if (!toSafeString(token, 500)) return false;\n if (isLocalAnalyticsHost(host) && !captureLocal) return false;\n return true;\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
2
|
import { InjectionToken, EnvironmentProviders } from '@angular/core';
|
|
3
3
|
|
|
4
|
-
type ThreadplaneTelemetryEvent = '
|
|
4
|
+
type ThreadplaneTelemetryEvent = 'tplane:browser_provided' | 'tplane:browser_chat_init' | 'tplane:runtime_instance_created' | 'tplane:runtime_request_created' | 'tplane:stream_started' | 'tplane:stream_ended' | 'tplane:stream_errored';
|
|
5
5
|
interface ThreadplaneTelemetryEventPayload {
|
|
6
6
|
event: ThreadplaneTelemetryEvent;
|
|
7
7
|
properties?: Record<string, unknown>;
|
package/node/adapter.js
CHANGED
|
@@ -9,22 +9,22 @@ export async function captureRuntimeInstanceCreated(input) {
|
|
|
9
9
|
await safe(async () => {
|
|
10
10
|
const { apiKey, ...rest } = input;
|
|
11
11
|
void apiKey;
|
|
12
|
-
await captureEvent('
|
|
12
|
+
await captureEvent('tplane:runtime_instance_created', { ...rest });
|
|
13
13
|
});
|
|
14
14
|
}
|
|
15
15
|
export async function captureRuntimeRequestCreated(input) {
|
|
16
|
-
await safe(() => captureEvent('
|
|
16
|
+
await safe(() => captureEvent('tplane:runtime_request_created', { ...input }));
|
|
17
17
|
}
|
|
18
18
|
export async function captureStreamStarted(input) {
|
|
19
|
-
await safe(() => captureEvent('
|
|
19
|
+
await safe(() => captureEvent('tplane:stream_started', { ...input }));
|
|
20
20
|
}
|
|
21
21
|
export async function captureStreamEnded(input) {
|
|
22
|
-
await safe(() => captureEvent('
|
|
22
|
+
await safe(() => captureEvent('tplane:stream_ended', { ...input }));
|
|
23
23
|
}
|
|
24
24
|
export async function captureStreamErrored(input) {
|
|
25
25
|
await safe(async () => {
|
|
26
26
|
const { error, ...rest } = input;
|
|
27
27
|
const errorClass = error instanceof Error ? error.constructor.name : 'Unknown';
|
|
28
|
-
await captureEvent('
|
|
28
|
+
await captureEvent('tplane:stream_errored', { ...rest, errorClass });
|
|
29
29
|
});
|
|
30
30
|
}
|
package/node/client.js
CHANGED
|
@@ -8,7 +8,7 @@ const REQUEST_TIMEOUT_MS = 3_000;
|
|
|
8
8
|
// server-side with the private PostHog token.
|
|
9
9
|
const PUBLIC_INGEST_KEY = 'phc_public_cacheplane_telemetry';
|
|
10
10
|
function getSampleRate(env = process.env) {
|
|
11
|
-
const parsed = Number(env.
|
|
11
|
+
const parsed = Number(env.TPLANE_TELEMETRY_SAMPLE_RATE ?? '1');
|
|
12
12
|
if (!Number.isFinite(parsed))
|
|
13
13
|
return 1;
|
|
14
14
|
return Math.max(0, Math.min(1, parsed));
|
|
@@ -94,7 +94,7 @@ export async function captureEvent(event, properties = {}) {
|
|
|
94
94
|
if (!shouldSample(rate, anonId))
|
|
95
95
|
return { sent: false, reason: 'sampled' };
|
|
96
96
|
try {
|
|
97
|
-
await postJson(process.env.
|
|
97
|
+
await postJson(process.env.TPLANE_TELEMETRY_INGEST_URL ?? DEFAULT_INGEST, {
|
|
98
98
|
key: PUBLIC_INGEST_KEY,
|
|
99
99
|
distinctId: anonId,
|
|
100
100
|
event,
|
|
@@ -110,7 +110,7 @@ export async function captureEvent(event, properties = {}) {
|
|
|
110
110
|
}
|
|
111
111
|
}
|
|
112
112
|
export async function capturePostinstall(input) {
|
|
113
|
-
return captureEvent('
|
|
113
|
+
return captureEvent('tplane:postinstall', createPostinstallProperties(input));
|
|
114
114
|
}
|
|
115
115
|
// @internal — tests only
|
|
116
116
|
export function _resetClientForTesting() {
|
package/node/postinstall.js
CHANGED
|
@@ -11,7 +11,7 @@ function truthy(value) {
|
|
|
11
11
|
function debugEnabled(env) {
|
|
12
12
|
return (env.DEBUG ?? '')
|
|
13
13
|
.split(/[\s,]+/)
|
|
14
|
-
.some((part) => part === '*' || part === '
|
|
14
|
+
.some((part) => part === '*' || part === 'tplane:*' || part === 'tplane:telemetry');
|
|
15
15
|
}
|
|
16
16
|
function isGlobalInstall(env) {
|
|
17
17
|
return truthy(env.npm_config_global) || env.npm_config_location === 'global';
|
|
@@ -43,7 +43,7 @@ function readLifecyclePackageJson(env, cwd) {
|
|
|
43
43
|
}
|
|
44
44
|
export async function capturePostinstallScript(deps) {
|
|
45
45
|
// Single opt-out gate. DO_NOT_TRACK, npm_config_do_not_track,
|
|
46
|
-
//
|
|
46
|
+
// TPLANE_TELEMETRY_DISABLED, and CI envs all return early: no event, no notice.
|
|
47
47
|
if (isTelemetryDisabled(deps.env))
|
|
48
48
|
return;
|
|
49
49
|
const cwd = deps.cwd?.() ?? process.cwd();
|
|
@@ -60,13 +60,13 @@ export async function capturePostinstallScript(deps) {
|
|
|
60
60
|
const result = await capturePostinstall({ pkg: pkg.name, version: pkg.version });
|
|
61
61
|
if (debugEnabled(deps.env)) {
|
|
62
62
|
deps.write(`@threadplane/telemetry payload: ${JSON.stringify({
|
|
63
|
-
event: '
|
|
63
|
+
event: 'tplane:postinstall',
|
|
64
64
|
properties: createPostinstallProperties({ pkg: pkg.name, version: pkg.version }, deps.env),
|
|
65
65
|
})}\n`);
|
|
66
66
|
}
|
|
67
67
|
if (result.sent) {
|
|
68
68
|
deps.write(`@threadplane/telemetry: install telemetry sent (${pkg.name}@${pkg.version}). ` +
|
|
69
|
-
`Disable: DO_NOT_TRACK=1 or
|
|
69
|
+
`Disable: DO_NOT_TRACK=1 or TPLANE_TELEMETRY_DISABLED=1. ` +
|
|
70
70
|
`See https://github.com/cacheplane/angular-agent-framework/blob/main/libs/telemetry/README.md\n`);
|
|
71
71
|
}
|
|
72
72
|
}
|
package/package.json
CHANGED
package/shared/env.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
type DisableReason = 'DO_NOT_TRACK' | '
|
|
1
|
+
type DisableReason = 'DO_NOT_TRACK' | 'TPLANE_TELEMETRY_DISABLED' | 'CI' | null;
|
|
2
2
|
export declare function getDisableReason(env?: NodeJS.ProcessEnv): DisableReason;
|
|
3
3
|
export declare function isTelemetryDisabled(env?: NodeJS.ProcessEnv): boolean;
|
|
4
4
|
export {};
|
package/shared/env.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../../../../../libs/telemetry/src/shared/env.ts"],"names":[],"mappings":"AAMA,KAAK,aAAa,GAAG,cAAc,GAAG,
|
|
1
|
+
{"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../../../../../libs/telemetry/src/shared/env.ts"],"names":[],"mappings":"AAMA,KAAK,aAAa,GAAG,cAAc,GAAG,2BAA2B,GAAG,IAAI,GAAG,IAAI,CAAC;AAEhF,wBAAgB,gBAAgB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,aAAa,CAepF;AAED,wBAAgB,mBAAmB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,OAAO,CAEjF"}
|
package/shared/env.js
CHANGED
|
@@ -6,8 +6,8 @@ export function getDisableReason(env = process.env) {
|
|
|
6
6
|
if (truthy(env.DO_NOT_TRACK) || truthy(env.npm_config_do_not_track) || truthy(env.NPM_CONFIG_DO_NOT_TRACK)) {
|
|
7
7
|
return 'DO_NOT_TRACK';
|
|
8
8
|
}
|
|
9
|
-
if (truthy(env.
|
|
10
|
-
return '
|
|
9
|
+
if (truthy(env.TPLANE_TELEMETRY_DISABLED))
|
|
10
|
+
return 'TPLANE_TELEMETRY_DISABLED';
|
|
11
11
|
if (truthy(env.CI) ||
|
|
12
12
|
truthy(env.GITHUB_ACTIONS) ||
|
|
13
13
|
truthy(env.CONTINUOUS_INTEGRATION) ||
|
package/shared/events.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type ThreadplaneNodeEvent = '
|
|
2
|
-
export type ThreadplaneBrowserEvent = '
|
|
1
|
+
export type ThreadplaneNodeEvent = 'tplane:postinstall' | 'tplane:runtime_instance_created' | 'tplane:runtime_request_created' | 'tplane:stream_started' | 'tplane:stream_ended' | 'tplane:stream_errored';
|
|
2
|
+
export type ThreadplaneBrowserEvent = 'tplane:browser_provided' | 'tplane:browser_chat_init';
|
|
3
3
|
export type ThreadplaneEvent = ThreadplaneNodeEvent | ThreadplaneBrowserEvent;
|
|
4
4
|
//# sourceMappingURL=events.d.ts.map
|
package/shared/events.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../../../../libs/telemetry/src/shared/events.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,oBAAoB,GAC5B,
|
|
1
|
+
{"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../../../../libs/telemetry/src/shared/events.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,oBAAoB,GAC5B,oBAAoB,GACpB,iCAAiC,GACjC,gCAAgC,GAChC,uBAAuB,GACvB,qBAAqB,GACrB,uBAAuB,CAAC;AAE5B,MAAM,MAAM,uBAAuB,GAC/B,yBAAyB,GACzB,0BAA0B,CAAC;AAE/B,MAAM,MAAM,gBAAgB,GAAG,oBAAoB,GAAG,uBAAuB,CAAC"}
|