great-cto 2.5.8 → 2.5.9
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/dist/telemetry.js +52 -94
- package/package.json +1 -1
package/dist/telemetry.js
CHANGED
|
@@ -1,42 +1,27 @@
|
|
|
1
1
|
// Anonymous opt-in telemetry.
|
|
2
2
|
//
|
|
3
|
-
// What we send: random install_id (UUID,
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
// ~/.great_cto/config.json.
|
|
3
|
+
// What we send: random install_id (UUID), version, archetype, node version,
|
|
4
|
+
// platform, and timestamp. Nothing personal — no email, paths, code, or repo
|
|
5
|
+
// names. The install_id is generated once and stored in ~/.great_cto/config.json.
|
|
7
6
|
//
|
|
8
7
|
// What we DON'T send: project paths, code, file names, environment variables,
|
|
9
|
-
// shell history,
|
|
10
|
-
// country-level geo is retained at ingestion).
|
|
8
|
+
// shell history, IP-derived geolocation (CF only logs country at the edge).
|
|
11
9
|
//
|
|
12
10
|
// Opt-out:
|
|
13
11
|
// - GREATCTO_NO_TELEMETRY=1 env var (highest priority)
|
|
14
|
-
// - DO_NOT_TRACK=1 env var (consoledonottrack.com standard)
|
|
15
12
|
// - --no-telemetry CLI flag
|
|
16
13
|
// - User declines the first-run prompt
|
|
17
14
|
// - Manually edit ~/.great_cto/config.json: { "telemetry": false }
|
|
18
15
|
//
|
|
19
|
-
//
|
|
20
|
-
//
|
|
21
|
-
// Endpoint: POST /i/v0/e/ (single-event capture)
|
|
22
|
-
// API key: phc_* — public, write-only ingestion token; safe in OSS code.
|
|
23
|
-
//
|
|
24
|
-
// We never run telemetry from an `npm postinstall` hook. The install ping fires
|
|
25
|
-
// from the first interactive `great-cto init` run instead, so a failed network
|
|
26
|
-
// call can never break `npm install` / `npm ci`.
|
|
16
|
+
// Endpoint: https://greatcto.systems/api/install (Cloudflare Worker → D1)
|
|
17
|
+
// Source: workers/telemetry/index.js
|
|
27
18
|
import * as fs from "node:fs";
|
|
28
19
|
import * as path from "node:path";
|
|
29
20
|
import * as os from "node:os";
|
|
30
21
|
import * as crypto from "node:crypto";
|
|
31
22
|
import { dim, log } from "./ui.js";
|
|
32
|
-
|
|
33
|
-
// phc_* keys can only POST events, not read them.
|
|
34
|
-
const POSTHOG_API_KEY = "phc_xk55Ce6CMp9ZgjZHVjdGoshxZJk7kSF4WGXKLRUHatGv";
|
|
35
|
-
const POSTHOG_ENDPOINT = "https://eu.i.posthog.com/i/v0/e/";
|
|
23
|
+
const TELEMETRY_ENDPOINT = "https://greatcto.systems/api/install";
|
|
36
24
|
const TELEMETRY_TIMEOUT_MS = 1500;
|
|
37
|
-
function telemetryDisabledByEnv() {
|
|
38
|
-
return process.env.GREATCTO_NO_TELEMETRY === "1" || process.env.DO_NOT_TRACK === "1";
|
|
39
|
-
}
|
|
40
25
|
function configPath() {
|
|
41
26
|
return path.join(os.homedir(), ".great_cto", "config.json");
|
|
42
27
|
}
|
|
@@ -73,7 +58,7 @@ function ensureInstallId(cfg) {
|
|
|
73
58
|
* 4. Default to enabled (true) if non-interactive (e.g. CI), else show notice
|
|
74
59
|
*/
|
|
75
60
|
export function resolveTelemetryConsent(cliFlag) {
|
|
76
|
-
if (
|
|
61
|
+
if (process.env.GREATCTO_NO_TELEMETRY === "1")
|
|
77
62
|
return false;
|
|
78
63
|
if (cliFlag)
|
|
79
64
|
return false;
|
|
@@ -84,12 +69,11 @@ export function resolveTelemetryConsent(cliFlag) {
|
|
|
84
69
|
// show a clear notice with how to disable. Kept short; full details in README.
|
|
85
70
|
log("");
|
|
86
71
|
log(dim("─ Anonymous telemetry ────────────────────────────────"));
|
|
87
|
-
log(dim(" great_cto sends one anonymous ping per install
|
|
88
|
-
log(dim("
|
|
89
|
-
log(dim(" Sent: install_id, version, archetype, Node, OS, exit code."));
|
|
72
|
+
log(dim(" great_cto sends one anonymous ping per install:"));
|
|
73
|
+
log(dim(" install_id, version, archetype, Node version, OS."));
|
|
90
74
|
log(dim(" No paths, no code, no PII. Disable any time:"));
|
|
91
|
-
log(dim(" great-cto --no-telemetry ·
|
|
92
|
-
log(dim("
|
|
75
|
+
log(dim(" great-cto --no-telemetry · or set GREATCTO_NO_TELEMETRY=1"));
|
|
76
|
+
log(dim(" or edit ~/.great_cto/config.json: { \"telemetry\": false }"));
|
|
93
77
|
log(dim("──────────────────────────────────────────────────────"));
|
|
94
78
|
log("");
|
|
95
79
|
cfg.telemetry = true;
|
|
@@ -99,70 +83,38 @@ export function resolveTelemetryConsent(cliFlag) {
|
|
|
99
83
|
return true;
|
|
100
84
|
}
|
|
101
85
|
/**
|
|
102
|
-
*
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
* `distinct_id` is the random install_id (no PII).
|
|
86
|
+
* Best-effort telemetry ping. Non-blocking, fire-and-forget. Never throws.
|
|
87
|
+
* Returns a promise that resolves once the request completes or times out.
|
|
106
88
|
*/
|
|
107
|
-
async function
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
},
|
|
121
|
-
timestamp: new Date().toISOString(),
|
|
89
|
+
export async function sendInstallPing(opts) {
|
|
90
|
+
if (!opts.consent)
|
|
91
|
+
return;
|
|
92
|
+
const cfg = readConfig();
|
|
93
|
+
const install_id = ensureInstallId(cfg);
|
|
94
|
+
const evt = {
|
|
95
|
+
install_id,
|
|
96
|
+
cli_version: opts.cliVersion,
|
|
97
|
+
archetype: opts.archetype,
|
|
98
|
+
node_version: process.version,
|
|
99
|
+
platform: process.platform,
|
|
100
|
+
arch: process.arch,
|
|
101
|
+
ts: new Date().toISOString(),
|
|
122
102
|
};
|
|
123
103
|
try {
|
|
124
104
|
const ctrl = new AbortController();
|
|
125
105
|
const timer = setTimeout(() => ctrl.abort(), TELEMETRY_TIMEOUT_MS);
|
|
126
|
-
await fetch(
|
|
106
|
+
await fetch(TELEMETRY_ENDPOINT, {
|
|
127
107
|
method: "POST",
|
|
128
|
-
headers: {
|
|
129
|
-
|
|
130
|
-
"User-Agent": `great-cto-cli/${opts.cliVersion}`,
|
|
131
|
-
},
|
|
132
|
-
body: JSON.stringify(body),
|
|
108
|
+
headers: { "Content-Type": "application/json", "User-Agent": `great-cto-cli/${opts.cliVersion}` },
|
|
109
|
+
body: JSON.stringify(evt),
|
|
133
110
|
signal: ctrl.signal,
|
|
134
111
|
}).catch(() => { });
|
|
135
112
|
clearTimeout(timer);
|
|
136
113
|
}
|
|
137
114
|
catch {
|
|
138
|
-
//
|
|
115
|
+
// never block install on telemetry failure
|
|
139
116
|
}
|
|
140
117
|
}
|
|
141
|
-
/**
|
|
142
|
-
* Install / first-run ping. Fired once per `great-cto init` invocation.
|
|
143
|
-
* Non-blocking, fire-and-forget. Never throws.
|
|
144
|
-
*/
|
|
145
|
-
export async function sendInstallPing(opts) {
|
|
146
|
-
if (!opts.consent)
|
|
147
|
-
return;
|
|
148
|
-
if (telemetryDisabledByEnv())
|
|
149
|
-
return;
|
|
150
|
-
const cfg = readConfig();
|
|
151
|
-
const install_id = ensureInstallId(cfg);
|
|
152
|
-
await postHogCapture({
|
|
153
|
-
event: "cli_install",
|
|
154
|
-
distinct_id: install_id,
|
|
155
|
-
cliVersion: opts.cliVersion,
|
|
156
|
-
properties: {
|
|
157
|
-
cli_version: opts.cliVersion,
|
|
158
|
-
archetype: opts.archetype,
|
|
159
|
-
node_version: process.version,
|
|
160
|
-
platform: process.platform,
|
|
161
|
-
arch: process.arch,
|
|
162
|
-
ci: Boolean(process.env.CI),
|
|
163
|
-
},
|
|
164
|
-
});
|
|
165
|
-
}
|
|
166
118
|
/**
|
|
167
119
|
* Subcommand-usage ping. Fire-and-forget. Used to track which v2.4+ commands
|
|
168
120
|
* (ci / mcp / adapt / serve / report / webhook) actually get used in the wild.
|
|
@@ -177,25 +129,31 @@ export async function sendInstallPing(opts) {
|
|
|
177
129
|
* Honours the same opt-out signals as install ping.
|
|
178
130
|
*/
|
|
179
131
|
export async function sendUsagePing(opts) {
|
|
180
|
-
if (
|
|
132
|
+
if (process.env.GREATCTO_NO_TELEMETRY === "1")
|
|
181
133
|
return;
|
|
182
134
|
const cfg = readConfig();
|
|
183
135
|
if (cfg.telemetry === false)
|
|
184
136
|
return;
|
|
185
137
|
if (!cfg.install_id)
|
|
186
138
|
return; // never ping without an established install_id
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
139
|
+
try {
|
|
140
|
+
const ctrl = new AbortController();
|
|
141
|
+
const timer = setTimeout(() => ctrl.abort(), TELEMETRY_TIMEOUT_MS);
|
|
142
|
+
await fetch(`${TELEMETRY_ENDPOINT.replace("/install", "/usage")}`, {
|
|
143
|
+
method: "POST",
|
|
144
|
+
headers: { "Content-Type": "application/json", "User-Agent": `great-cto-cli/${opts.cliVersion}` },
|
|
145
|
+
body: JSON.stringify({
|
|
146
|
+
install_id: cfg.install_id,
|
|
147
|
+
cli_version: opts.cliVersion,
|
|
148
|
+
subcommand: opts.subcommand,
|
|
149
|
+
exit_code: opts.exitCode,
|
|
150
|
+
ts: new Date().toISOString(),
|
|
151
|
+
}),
|
|
152
|
+
signal: ctrl.signal,
|
|
153
|
+
}).catch(() => { });
|
|
154
|
+
clearTimeout(timer);
|
|
155
|
+
}
|
|
156
|
+
catch {
|
|
157
|
+
// best-effort
|
|
158
|
+
}
|
|
201
159
|
}
|
package/package.json
CHANGED