great-cto 2.5.1 → 2.5.2
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/main.js +20 -7
- package/dist/telemetry.js +42 -0
- package/package.json +1 -1
package/dist/main.js
CHANGED
|
@@ -16,7 +16,7 @@ import { pickArchetype, suggestCompliance } from "./archetypes.js";
|
|
|
16
16
|
import { install, findInstalledVersions } from "./installer.js";
|
|
17
17
|
import { enableGreatCto } from "./settings.js";
|
|
18
18
|
import { bootstrap } from "./bootstrap.js";
|
|
19
|
-
import { resolveTelemetryConsent, sendInstallPing } from "./telemetry.js";
|
|
19
|
+
import { resolveTelemetryConsent, sendInstallPing, sendUsagePing } from "./telemetry.js";
|
|
20
20
|
import { shouldUseLlmFallback, suggestArchetypeFromLlm } from "./llm-fallback.js";
|
|
21
21
|
import { readFileSync } from "node:fs";
|
|
22
22
|
import { dirname, join } from "node:path";
|
|
@@ -727,6 +727,19 @@ async function runInit(args) {
|
|
|
727
727
|
log("");
|
|
728
728
|
return 0;
|
|
729
729
|
}
|
|
730
|
+
/**
|
|
731
|
+
* Exit with telemetry ping for a subcommand. Fire-and-forget — we wait up
|
|
732
|
+
* to 200ms for the ping to land before exiting so it doesn't get killed by
|
|
733
|
+
* process termination. Honours all telemetry opt-out signals.
|
|
734
|
+
*/
|
|
735
|
+
async function exitWithTelemetry(subcommand, code) {
|
|
736
|
+
try {
|
|
737
|
+
const promise = sendUsagePing({ cliVersion: getCliVersion(), subcommand, exitCode: code });
|
|
738
|
+
await Promise.race([promise, new Promise(r => setTimeout(r, 200))]);
|
|
739
|
+
}
|
|
740
|
+
catch { /* never block exit */ }
|
|
741
|
+
process.exit(code);
|
|
742
|
+
}
|
|
730
743
|
async function main() {
|
|
731
744
|
const rawArgv = process.argv.slice(2);
|
|
732
745
|
const args = parseArgs(rawArgv);
|
|
@@ -778,7 +791,7 @@ async function main() {
|
|
|
778
791
|
try {
|
|
779
792
|
const { runCi, parseCiArgs } = await import("./ci.js");
|
|
780
793
|
const code = await runCi(parseCiArgs(rawArgv));
|
|
781
|
-
|
|
794
|
+
await exitWithTelemetry("ci", code);
|
|
782
795
|
}
|
|
783
796
|
catch (e) {
|
|
784
797
|
error(e.message);
|
|
@@ -792,7 +805,7 @@ async function main() {
|
|
|
792
805
|
const portArg = rawArgv.indexOf("--port");
|
|
793
806
|
const port = portArg >= 0 ? parseInt(rawArgv[portArg + 1] ?? "8765", 10) : 8765;
|
|
794
807
|
const code = await runMcp({ mode: sse ? "sse" : "stdio", port, version: getCliVersion() });
|
|
795
|
-
|
|
808
|
+
await exitWithTelemetry("mcp", code);
|
|
796
809
|
}
|
|
797
810
|
catch (e) {
|
|
798
811
|
error(e.message);
|
|
@@ -814,7 +827,7 @@ async function main() {
|
|
|
814
827
|
dryRun: rawArgv.includes("--dry-run"),
|
|
815
828
|
cwd: args.dir,
|
|
816
829
|
});
|
|
817
|
-
|
|
830
|
+
await exitWithTelemetry("adapt", code);
|
|
818
831
|
}
|
|
819
832
|
catch (e) {
|
|
820
833
|
error(e.message);
|
|
@@ -829,7 +842,7 @@ async function main() {
|
|
|
829
842
|
noLog: rawArgv.includes("--no-log"),
|
|
830
843
|
insecure: rawArgv.includes("--insecure"),
|
|
831
844
|
});
|
|
832
|
-
|
|
845
|
+
await exitWithTelemetry("serve", code);
|
|
833
846
|
}
|
|
834
847
|
catch (e) {
|
|
835
848
|
error(e.message);
|
|
@@ -845,7 +858,7 @@ async function main() {
|
|
|
845
858
|
process.exit(2);
|
|
846
859
|
}
|
|
847
860
|
const code = await runWebhookCli(parsed);
|
|
848
|
-
|
|
861
|
+
await exitWithTelemetry("webhook", code);
|
|
849
862
|
}
|
|
850
863
|
catch (e) {
|
|
851
864
|
error(e.message);
|
|
@@ -860,7 +873,7 @@ async function main() {
|
|
|
860
873
|
process.exit(2);
|
|
861
874
|
}
|
|
862
875
|
const code = await runReport(parsed);
|
|
863
|
-
|
|
876
|
+
await exitWithTelemetry("report", code);
|
|
864
877
|
}
|
|
865
878
|
catch (e) {
|
|
866
879
|
error(e.message);
|
package/dist/telemetry.js
CHANGED
|
@@ -115,3 +115,45 @@ export async function sendInstallPing(opts) {
|
|
|
115
115
|
// never block install on telemetry failure
|
|
116
116
|
}
|
|
117
117
|
}
|
|
118
|
+
/**
|
|
119
|
+
* Subcommand-usage ping. Fire-and-forget. Used to track which v2.4+ commands
|
|
120
|
+
* (ci / mcp / adapt / serve / report / webhook) actually get used in the wild.
|
|
121
|
+
*
|
|
122
|
+
* Sends only:
|
|
123
|
+
* - install_id (random UUID, set on first install)
|
|
124
|
+
* - cli_version
|
|
125
|
+
* - subcommand name
|
|
126
|
+
* - exit code (0 / 1 / 2)
|
|
127
|
+
*
|
|
128
|
+
* No paths, no flags (since flags often contain user input), no archetype.
|
|
129
|
+
* Honours the same opt-out signals as install ping.
|
|
130
|
+
*/
|
|
131
|
+
export async function sendUsagePing(opts) {
|
|
132
|
+
if (process.env.GREATCTO_NO_TELEMETRY === "1")
|
|
133
|
+
return;
|
|
134
|
+
const cfg = readConfig();
|
|
135
|
+
if (cfg.telemetry === false)
|
|
136
|
+
return;
|
|
137
|
+
if (!cfg.install_id)
|
|
138
|
+
return; // never ping without an established install_id
|
|
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
|
+
}
|
|
159
|
+
}
|
package/package.json
CHANGED