great-cto 2.9.1 → 2.9.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 +2 -108
- package/package.json +1 -1
- package/dist/telemetry.js +0 -225
package/dist/main.js
CHANGED
|
@@ -10,18 +10,16 @@
|
|
|
10
10
|
// 7. bootstrap .great_cto/PROJECT.md
|
|
11
11
|
// 8. print next steps
|
|
12
12
|
import { resolve } from "node:path";
|
|
13
|
-
import { banner, bold, cyan, dim, error,
|
|
13
|
+
import { banner, bold, cyan, dim, error, green, log, step, warn, yellow, confirm } from "./ui.js";
|
|
14
14
|
import { detect } from "./detect.js";
|
|
15
15
|
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
19
|
import { shouldUseLlmFallback, suggestArchetypeFromLlm } from "./llm-fallback.js";
|
|
20
|
-
import {
|
|
21
|
-
import { readFileSync, writeFileSync, mkdirSync, existsSync as fsExistsSync } from "node:fs";
|
|
20
|
+
import { readFileSync } from "node:fs";
|
|
22
21
|
import { dirname, join } from "node:path";
|
|
23
22
|
import { fileURLToPath } from "node:url";
|
|
24
|
-
import { homedir } from "node:os";
|
|
25
23
|
function getCliVersion() {
|
|
26
24
|
try {
|
|
27
25
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
@@ -96,8 +94,6 @@ function parseArgs(argv) {
|
|
|
96
94
|
args.command = "webhook";
|
|
97
95
|
else if (a === "report")
|
|
98
96
|
args.command = "report";
|
|
99
|
-
else if (a === "telemetry")
|
|
100
|
-
args.command = "telemetry";
|
|
101
97
|
// Slash-commands surfaced as CLI subcommands so users get a clear hint
|
|
102
98
|
// instead of a confusing usage error. These work only in the chat plugin.
|
|
103
99
|
else if (a === "start" || a === "audit" || a === "inbox" || a === "digest" ||
|
|
@@ -398,13 +394,6 @@ ${bold("Claude Code adapter:")}
|
|
|
398
394
|
great-cto adapt --dry-run Preview what would be written
|
|
399
395
|
${dim("Idempotent — re-run after editing .great_cto/PROJECT.md")}
|
|
400
396
|
|
|
401
|
-
${bold("Telemetry (opt-IN, off by default):")}
|
|
402
|
-
great-cto telemetry status Show current state + endpoint + anon_id
|
|
403
|
-
great-cto telemetry on Enable anonymous usage events
|
|
404
|
-
great-cto telemetry off Disable (also: DO_NOT_TRACK=1)
|
|
405
|
-
great-cto telemetry whoami Print your anon_id (8 hex chars)
|
|
406
|
-
${dim("Privacy: docs/PRIVACY.md · no code, no repo names, no PII")}
|
|
407
|
-
|
|
408
397
|
${bold("Webhook server (preview):")}
|
|
409
398
|
great-cto serve --port 3142 Webhook receiver (logs to ~/.great_cto/webhook-events.log)
|
|
410
399
|
${dim("Endpoints: POST /webhook/github, POST /webhook/generic, GET /events, GET /healthz")}
|
|
@@ -761,19 +750,6 @@ async function runInit(args) {
|
|
|
761
750
|
log("");
|
|
762
751
|
log(green(bold("✓ great_cto is ready.")));
|
|
763
752
|
log("");
|
|
764
|
-
// ── 6. opt-IN telemetry prompt ───────────────────────────
|
|
765
|
-
// Aggressive opt-IN promo (Option A from telemetry strategy).
|
|
766
|
-
// Shows up only when interactive + not CI + not already decided + DO_NOT_TRACK unset.
|
|
767
|
-
await promoteTelemetryOptIn({ archetype: archetype, cliVersion: getCliVersion(), yes: args.yes });
|
|
768
|
-
// If telemetry is enabled (either via prior opt-in or env var), fire a fresh
|
|
769
|
-
// install-ping so re-runs of `init` (after upgrades) count toward WAU/MAU.
|
|
770
|
-
if (isTelemetryEnabled()) {
|
|
771
|
-
await sendInstallPing({
|
|
772
|
-
cliVersion: getCliVersion(),
|
|
773
|
-
archetype: archetype,
|
|
774
|
-
consent: true,
|
|
775
|
-
}).catch(() => { });
|
|
776
|
-
}
|
|
777
753
|
log(bold("Next steps:"));
|
|
778
754
|
log(` 1. ${dim("Restart Claude Code to pick up the plugin.")}`);
|
|
779
755
|
log(` 2. ${dim("Edit")} ${cyan(".great_cto/PROJECT.md")} ${dim("to refine goals and compliance.")}`);
|
|
@@ -785,82 +761,6 @@ async function runInit(args) {
|
|
|
785
761
|
log("");
|
|
786
762
|
return 0;
|
|
787
763
|
}
|
|
788
|
-
// ── Telemetry opt-IN prompt ────────────────────────────────────────────────
|
|
789
|
-
// Shows after a successful init when interactive. NOT shown if:
|
|
790
|
-
// - --yes / -y was used (skip-confirmation mode)
|
|
791
|
-
// - DO_NOT_TRACK=1 (respect honest signal)
|
|
792
|
-
// - Already opted in or explicitly opted out (no nag)
|
|
793
|
-
// - CI environment
|
|
794
|
-
// - Non-TTY stdin (piped install)
|
|
795
|
-
async function promoteTelemetryOptIn(opts) {
|
|
796
|
-
// Skip prompts in non-interactive mode.
|
|
797
|
-
if (opts.yes)
|
|
798
|
-
return;
|
|
799
|
-
if (!process.stdin.isTTY)
|
|
800
|
-
return;
|
|
801
|
-
if (process.env.DO_NOT_TRACK === "1" || process.env.DO_NOT_TRACK === "true")
|
|
802
|
-
return;
|
|
803
|
-
if (process.env.CI || process.env.GITHUB_ACTIONS)
|
|
804
|
-
return;
|
|
805
|
-
// If user already made a decision (either way), respect it — don't re-ask.
|
|
806
|
-
const cfgFile = join(homedir(), ".great_cto", "telemetry.json");
|
|
807
|
-
if (fsExistsSync(cfgFile)) {
|
|
808
|
-
try {
|
|
809
|
-
const cfg = JSON.parse(readFileSync(cfgFile, "utf8"));
|
|
810
|
-
if (cfg.enabled === true || cfg.enabled === false)
|
|
811
|
-
return;
|
|
812
|
-
}
|
|
813
|
-
catch { /* malformed — ask again */ }
|
|
814
|
-
}
|
|
815
|
-
// The honest, brand-aligned ask.
|
|
816
|
-
log(dim("─".repeat(60)));
|
|
817
|
-
log(bold("Help great_cto learn from how you use it?"));
|
|
818
|
-
log("");
|
|
819
|
-
log(dim("Anonymous usage data (default: off). Helps cross-project"));
|
|
820
|
-
log(dim("lessons promote to a global pattern library."));
|
|
821
|
-
log("");
|
|
822
|
-
log(dim("Here is exactly what would be sent — one event per command:"));
|
|
823
|
-
log("");
|
|
824
|
-
log(gray(` {`));
|
|
825
|
-
log(gray(` "version": "${opts.cliVersion}",`));
|
|
826
|
-
log(gray(` "command": "init",`));
|
|
827
|
-
log(gray(` "archetype": "${opts.archetype}",`));
|
|
828
|
-
log(gray(` "node": "${process.version.replace(/^v/, "")}",`));
|
|
829
|
-
log(gray(` "os": "${process.platform}",`));
|
|
830
|
-
log(gray(` "exit_code": 0,`));
|
|
831
|
-
log(gray(` "duration_ms": 1234,`));
|
|
832
|
-
log(gray(` "anon_id": "${computeAnonId()}" ${dim("// 8 hex chars, not reversible")}`));
|
|
833
|
-
log(gray(` }`));
|
|
834
|
-
log("");
|
|
835
|
-
log(dim("No code, no repo names, no file paths, no PII."));
|
|
836
|
-
log(dim("Toggle anytime: " + cyan("npx great-cto telemetry off")));
|
|
837
|
-
log(dim("Privacy: " + cyan("github.com/avelikiy/great_cto/blob/main/docs/PRIVACY.md")));
|
|
838
|
-
log("");
|
|
839
|
-
const yes = await confirm(bold("Enable anonymous telemetry?"), false);
|
|
840
|
-
log("");
|
|
841
|
-
// Persist the decision either way so we never re-ask.
|
|
842
|
-
try {
|
|
843
|
-
const dir = join(homedir(), ".great_cto");
|
|
844
|
-
mkdirSync(dir, { recursive: true });
|
|
845
|
-
writeFileSync(join(dir, "telemetry.json"), JSON.stringify({ enabled: yes, decided_at: new Date().toISOString() }, null, 2) + "\n");
|
|
846
|
-
}
|
|
847
|
-
catch { /* best-effort */ }
|
|
848
|
-
if (yes) {
|
|
849
|
-
log(green(`✓ Telemetry enabled. Thank you.`));
|
|
850
|
-
log(dim(` See your anon_id anytime: ${cyan("npx great-cto telemetry whoami")}`));
|
|
851
|
-
log("");
|
|
852
|
-
// Send the first event — the install-ping itself.
|
|
853
|
-
await sendInstallPing({
|
|
854
|
-
cliVersion: opts.cliVersion,
|
|
855
|
-
archetype: opts.archetype,
|
|
856
|
-
consent: true,
|
|
857
|
-
});
|
|
858
|
-
}
|
|
859
|
-
else {
|
|
860
|
-
log(dim(`Telemetry off. ${cyan("npx great-cto telemetry on")} to change later.`));
|
|
861
|
-
log("");
|
|
862
|
-
}
|
|
863
|
-
}
|
|
864
764
|
async function main() {
|
|
865
765
|
const rawArgv = process.argv.slice(2);
|
|
866
766
|
const args = parseArgs(rawArgv);
|
|
@@ -875,12 +775,6 @@ async function main() {
|
|
|
875
775
|
log(`Run ${cyan("great-cto --help")} for usage.`);
|
|
876
776
|
process.exit(2);
|
|
877
777
|
}
|
|
878
|
-
if (args.command === "telemetry") {
|
|
879
|
-
const sub = rawArgv[rawArgv.indexOf("telemetry") + 1];
|
|
880
|
-
const { exitCode, output } = telemetrySubcommand(sub);
|
|
881
|
-
process.stdout.write(output);
|
|
882
|
-
process.exit(exitCode);
|
|
883
|
-
}
|
|
884
778
|
if (args.command === "scan") {
|
|
885
779
|
try {
|
|
886
780
|
const code = await runScan(args, rawArgv);
|
package/package.json
CHANGED
package/dist/telemetry.js
DELETED
|
@@ -1,225 +0,0 @@
|
|
|
1
|
-
// Anonymous opt-IN telemetry — default OFF.
|
|
2
|
-
//
|
|
3
|
-
// See docs/PRIVACY.md for the full policy. Short version:
|
|
4
|
-
// - Default: disabled (opt-in)
|
|
5
|
-
// - Honors DO_NOT_TRACK=1 (industry standard, https://consoledonottrack.com)
|
|
6
|
-
// - Skipped automatically in CI environments
|
|
7
|
-
// - No paths, no code, no PII — just {ts, version, command, archetype, node, os, exit, duration_ms, anon_id}
|
|
8
|
-
// - anon_id is sha256(user@hostname) truncated to 8 hex chars; not reversible
|
|
9
|
-
//
|
|
10
|
-
// Opt-in (any one):
|
|
11
|
-
// GREAT_CTO_TELEMETRY=on (env var)
|
|
12
|
-
// ~/.great_cto/telemetry.json: { "enabled": true }
|
|
13
|
-
// npx great-cto telemetry on
|
|
14
|
-
//
|
|
15
|
-
// Opt-out (overrides everything):
|
|
16
|
-
// DO_NOT_TRACK=1 (highest priority)
|
|
17
|
-
// GREAT_CTO_TELEMETRY=off
|
|
18
|
-
// GREAT_CTO_DISABLE_TELEMETRY=1 (legacy alias from v2.x)
|
|
19
|
-
// GREATCTO_NO_TELEMETRY=1 (legacy alias from v2.x)
|
|
20
|
-
// ~/.great_cto/telemetry.json: { "enabled": false }
|
|
21
|
-
//
|
|
22
|
-
// Endpoint: https://telemetry.greatcto.systems/v1/event (Cloudflare Worker → D1)
|
|
23
|
-
// Worker: workers/telemetry/index.ts
|
|
24
|
-
// Schema v1: see docs/PRIVACY.md "What we collect"
|
|
25
|
-
import * as fs from "node:fs";
|
|
26
|
-
import * as path from "node:path";
|
|
27
|
-
import * as os from "node:os";
|
|
28
|
-
import * as crypto from "node:crypto";
|
|
29
|
-
const TELEMETRY_ENDPOINT = process.env.GREAT_CTO_TELEMETRY_ENDPOINT
|
|
30
|
-
|| "https://great-cto-telemetry.alexander-velikiy.workers.dev/v1/event";
|
|
31
|
-
// Note: workers.dev URL is the temporary default until telemetry.greatcto.systems
|
|
32
|
-
// custom domain is bound. Override anytime with GREAT_CTO_TELEMETRY_ENDPOINT.
|
|
33
|
-
const TELEMETRY_TIMEOUT_MS = 1000;
|
|
34
|
-
// Allowlist — anything else is dropped client-side and server-side.
|
|
35
|
-
const ALLOWED_COMMANDS = new Set([
|
|
36
|
-
"init", "scan", "ci", "list-rules", "board", "register",
|
|
37
|
-
"adapt", "mcp", "report", "serve", "webhook",
|
|
38
|
-
"version", "help", "telemetry",
|
|
39
|
-
]);
|
|
40
|
-
// Allowlist for archetype field. Match the 25 documented + "none" + "unknown".
|
|
41
|
-
const ALLOWED_ARCHETYPES = new Set([
|
|
42
|
-
"none", "unknown", "greenfield",
|
|
43
|
-
"enterprise-saas", "agent-product", "ai-system", "mlops",
|
|
44
|
-
"cli-tool", "cli", "library", "sdk", "devtools",
|
|
45
|
-
"fintech", "regulated", "compliance",
|
|
46
|
-
"iot-embedded", "web3", "marketplace", "cms", "edtech",
|
|
47
|
-
"gov-public", "insurance", "data-platform", "streaming",
|
|
48
|
-
"mobile-app", "infra", "web-service", "agent",
|
|
49
|
-
]);
|
|
50
|
-
function configPath() {
|
|
51
|
-
return path.join(os.homedir(), ".great_cto", "telemetry.json");
|
|
52
|
-
}
|
|
53
|
-
function legacyConfigPath() {
|
|
54
|
-
return path.join(os.homedir(), ".great_cto", "config.json");
|
|
55
|
-
}
|
|
56
|
-
function readConfig() {
|
|
57
|
-
// Try new file first.
|
|
58
|
-
try {
|
|
59
|
-
return JSON.parse(fs.readFileSync(configPath(), "utf8"));
|
|
60
|
-
}
|
|
61
|
-
catch { /* fall through */ }
|
|
62
|
-
// Fall back to legacy config.json (read-only — never write to it).
|
|
63
|
-
try {
|
|
64
|
-
const legacy = JSON.parse(fs.readFileSync(legacyConfigPath(), "utf8"));
|
|
65
|
-
return { enabled: legacy.telemetry, install_id: legacy.install_id };
|
|
66
|
-
}
|
|
67
|
-
catch {
|
|
68
|
-
return {};
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
function writeConfig(cfg) {
|
|
72
|
-
const file = configPath();
|
|
73
|
-
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
74
|
-
fs.writeFileSync(file, JSON.stringify(cfg, null, 2) + "\n");
|
|
75
|
-
}
|
|
76
|
-
/** Detect CI / automation environments — never send from these. */
|
|
77
|
-
function isCI() {
|
|
78
|
-
const flags = ["CI", "GITHUB_ACTIONS", "GITLAB_CI", "CIRCLECI", "BUILDKITE",
|
|
79
|
-
"JENKINS_URL", "TF_BUILD", "DRONE", "TRAVIS", "APPVEYOR",
|
|
80
|
-
"BITBUCKET_BUILD_NUMBER", "TEAMCITY_VERSION", "CODEBUILD_BUILD_ID"];
|
|
81
|
-
return flags.some(f => process.env[f] != null && process.env[f] !== "");
|
|
82
|
-
}
|
|
83
|
-
/** Compute anon_id deterministically per machine, never reversible. */
|
|
84
|
-
export function computeAnonId() {
|
|
85
|
-
const seed = `great_cto/${os.userInfo().username || "?"}/${os.hostname() || "?"}`;
|
|
86
|
-
return crypto.createHash("sha256").update(seed).digest("hex").slice(0, 8);
|
|
87
|
-
}
|
|
88
|
-
/** Resolve telemetry-enabled state. Pure function, no side effects. */
|
|
89
|
-
export function isTelemetryEnabled(cliFlag = false) {
|
|
90
|
-
// Opt-out wins, in priority order:
|
|
91
|
-
if (process.env.DO_NOT_TRACK === "1" || process.env.DO_NOT_TRACK === "true")
|
|
92
|
-
return false;
|
|
93
|
-
if (process.env.GREAT_CTO_TELEMETRY === "off")
|
|
94
|
-
return false;
|
|
95
|
-
if (process.env.GREAT_CTO_DISABLE_TELEMETRY === "1")
|
|
96
|
-
return false;
|
|
97
|
-
if (process.env.GREATCTO_NO_TELEMETRY === "1")
|
|
98
|
-
return false;
|
|
99
|
-
if (cliFlag)
|
|
100
|
-
return false;
|
|
101
|
-
if (isCI())
|
|
102
|
-
return false;
|
|
103
|
-
// Opt-in checks:
|
|
104
|
-
if (process.env.GREAT_CTO_TELEMETRY === "on")
|
|
105
|
-
return true;
|
|
106
|
-
const cfg = readConfig();
|
|
107
|
-
if (cfg.enabled === true)
|
|
108
|
-
return true;
|
|
109
|
-
if (cfg.telemetry === true)
|
|
110
|
-
return true; // legacy
|
|
111
|
-
// Default: opt-out.
|
|
112
|
-
return false;
|
|
113
|
-
}
|
|
114
|
-
function sanitize(opts) {
|
|
115
|
-
const command = opts.command.toLowerCase();
|
|
116
|
-
if (!ALLOWED_COMMANDS.has(command))
|
|
117
|
-
return null;
|
|
118
|
-
const archetypeRaw = (opts.archetype || "none").toLowerCase().trim();
|
|
119
|
-
const archetype = ALLOWED_ARCHETYPES.has(archetypeRaw) ? archetypeRaw : "unknown";
|
|
120
|
-
return {
|
|
121
|
-
ts: new Date().toISOString(),
|
|
122
|
-
version: opts.cliVersion,
|
|
123
|
-
command,
|
|
124
|
-
archetype,
|
|
125
|
-
node: process.version.replace(/^v/, ""),
|
|
126
|
-
os: process.platform,
|
|
127
|
-
exit_code: typeof opts.exitCode === "number" ? opts.exitCode : 0,
|
|
128
|
-
duration_ms: typeof opts.durationMs === "number" ? Math.max(0, Math.round(opts.durationMs)) : 0,
|
|
129
|
-
anon_id: computeAnonId(),
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
|
-
/** Fire-and-forget POST. Never blocks. Never throws. Never logs unless DRYRUN. */
|
|
133
|
-
async function send(evt) {
|
|
134
|
-
if (process.env.GREAT_CTO_TELEMETRY_DRYRUN === "1") {
|
|
135
|
-
process.stderr.write(`[telemetry] would-send: ${JSON.stringify(evt)}\n`);
|
|
136
|
-
return;
|
|
137
|
-
}
|
|
138
|
-
try {
|
|
139
|
-
const ctrl = new AbortController();
|
|
140
|
-
const timer = setTimeout(() => ctrl.abort(), TELEMETRY_TIMEOUT_MS);
|
|
141
|
-
await fetch(TELEMETRY_ENDPOINT, {
|
|
142
|
-
method: "POST",
|
|
143
|
-
headers: { "Content-Type": "application/json" },
|
|
144
|
-
body: JSON.stringify(evt),
|
|
145
|
-
signal: ctrl.signal,
|
|
146
|
-
}).catch(() => { });
|
|
147
|
-
clearTimeout(timer);
|
|
148
|
-
}
|
|
149
|
-
catch { /* best-effort */ }
|
|
150
|
-
}
|
|
151
|
-
// --- Public API ------------------------------------------------------------
|
|
152
|
-
/** First-run/install ping. Sent only when enabled. Idempotent across runs. */
|
|
153
|
-
export async function sendInstallPing(opts) {
|
|
154
|
-
if (!opts.consent)
|
|
155
|
-
return;
|
|
156
|
-
if (!isTelemetryEnabled())
|
|
157
|
-
return;
|
|
158
|
-
const evt = sanitize({ cliVersion: opts.cliVersion, command: "init", archetype: opts.archetype });
|
|
159
|
-
if (!evt)
|
|
160
|
-
return;
|
|
161
|
-
await send(evt);
|
|
162
|
-
}
|
|
163
|
-
/** Per-command usage ping. Sent only when enabled. Fire-and-forget. */
|
|
164
|
-
export async function sendUsagePing(opts) {
|
|
165
|
-
if (!isTelemetryEnabled())
|
|
166
|
-
return;
|
|
167
|
-
const evt = sanitize({
|
|
168
|
-
cliVersion: opts.cliVersion,
|
|
169
|
-
command: opts.subcommand,
|
|
170
|
-
archetype: opts.archetype,
|
|
171
|
-
exitCode: opts.exitCode,
|
|
172
|
-
durationMs: opts.durationMs,
|
|
173
|
-
});
|
|
174
|
-
if (!evt)
|
|
175
|
-
return;
|
|
176
|
-
await send(evt);
|
|
177
|
-
}
|
|
178
|
-
/**
|
|
179
|
-
* Legacy shim — preserved for backwards compatibility with callers in main.ts
|
|
180
|
-
* that pass `--no-telemetry`. With opt-IN default, consent resolution is
|
|
181
|
-
* trivial: enabled iff isTelemetryEnabled() returns true.
|
|
182
|
-
*/
|
|
183
|
-
export function resolveTelemetryConsent(cliFlag) {
|
|
184
|
-
return isTelemetryEnabled(cliFlag);
|
|
185
|
-
}
|
|
186
|
-
// --- `npx great-cto telemetry <on|off|status|whoami>` subcommand -----------
|
|
187
|
-
export function telemetrySubcommand(arg) {
|
|
188
|
-
const action = (arg || "status").toLowerCase();
|
|
189
|
-
switch (action) {
|
|
190
|
-
case "on": {
|
|
191
|
-
const cfg = readConfig();
|
|
192
|
-
cfg.enabled = true;
|
|
193
|
-
writeConfig(cfg);
|
|
194
|
-
return { exitCode: 0, output: `✓ telemetry enabled (config: ${configPath()})\n` +
|
|
195
|
-
` Anonymous events go to ${TELEMETRY_ENDPOINT}\n` +
|
|
196
|
-
` See docs/PRIVACY.md for the full data schema.\n` };
|
|
197
|
-
}
|
|
198
|
-
case "off": {
|
|
199
|
-
const cfg = readConfig();
|
|
200
|
-
cfg.enabled = false;
|
|
201
|
-
writeConfig(cfg);
|
|
202
|
-
return { exitCode: 0, output: `✓ telemetry disabled (config: ${configPath()})\n` };
|
|
203
|
-
}
|
|
204
|
-
case "status": {
|
|
205
|
-
const enabled = isTelemetryEnabled();
|
|
206
|
-
const reason = enabled
|
|
207
|
-
? "enabled (sending events to " + TELEMETRY_ENDPOINT + ")"
|
|
208
|
-
: isCI()
|
|
209
|
-
? "disabled (CI environment detected)"
|
|
210
|
-
: process.env.DO_NOT_TRACK === "1"
|
|
211
|
-
? "disabled (DO_NOT_TRACK=1)"
|
|
212
|
-
: "disabled (default; run 'great-cto telemetry on' to enable)";
|
|
213
|
-
return { exitCode: 0, output: `telemetry: ${reason}\n` +
|
|
214
|
-
`anon_id : ${computeAnonId()}\n` +
|
|
215
|
-
`endpoint : ${TELEMETRY_ENDPOINT}\n` +
|
|
216
|
-
`config : ${configPath()}\n` };
|
|
217
|
-
}
|
|
218
|
-
case "whoami": {
|
|
219
|
-
return { exitCode: 0, output: computeAnonId() + "\n" };
|
|
220
|
-
}
|
|
221
|
-
default: {
|
|
222
|
-
return { exitCode: 2, output: `usage: great-cto telemetry <on|off|status|whoami>\n` };
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
}
|