@nuvio/cli 0.5.3 → 0.5.4
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 +12 -0
- package/dist/cli-entry.js +218 -3
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -8,3 +8,15 @@ pnpm dev
|
|
|
8
8
|
```
|
|
9
9
|
|
|
10
10
|
See [Nuvio docs](https://github.com/ehah/Nuvio/blob/main/docs/nuvioUser.md).
|
|
11
|
+
|
|
12
|
+
## Telemetry
|
|
13
|
+
|
|
14
|
+
Nuvio collects anonymous usage metrics to improve onboarding and reliability. No source code, file contents, file paths, project names, emails, or personal data are sent.
|
|
15
|
+
|
|
16
|
+
Disable anytime with:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
NUVIO_TELEMETRY=0
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
See [PostHog_telemetry.md](https://github.com/ehah/Nuvio/blob/main/docs/PostHog_telemetry.md).
|
package/dist/cli-entry.js
CHANGED
|
@@ -20,7 +20,12 @@ var MSG = {
|
|
|
20
20
|
monorepoRoot: "This looks like the Nuvio monorepo. Run init in your app folder, not the tooling repo.",
|
|
21
21
|
cliPackage: "Cannot init inside @nuvio/cli package.",
|
|
22
22
|
partialHelp: "Nuvio set up what it could safely. Finish the steps in nuvio/SETUP_TODO.md, then run your dev server.",
|
|
23
|
-
noHeading: 'Nuvio is wired, but I could not find a heading to mark editable. Add data-nuvio-id="page.title" to one visible element (see nuvio/START_HERE.md).'
|
|
23
|
+
noHeading: 'Nuvio is wired, but I could not find a heading to mark editable. Add data-nuvio-id="page.title" to one visible element (see nuvio/START_HERE.md).',
|
|
24
|
+
telemetryNotice: `Nuvio collects anonymous usage metrics to improve onboarding and reliability.
|
|
25
|
+
No source code, file contents, file paths, project names, emails, or personal data are sent.
|
|
26
|
+
|
|
27
|
+
Disable anytime with:
|
|
28
|
+
NUVIO_TELEMETRY=0`
|
|
24
29
|
};
|
|
25
30
|
|
|
26
31
|
// src/detect-project.ts
|
|
@@ -692,6 +697,165 @@ function printVerification(v) {
|
|
|
692
697
|
console.log(` Starter id page.title \u2014 ${v.starterId}`);
|
|
693
698
|
}
|
|
694
699
|
|
|
700
|
+
// src/telemetry.ts
|
|
701
|
+
import { mkdirSync as mkdirSync2, readFileSync as readFileSync10, writeFileSync as writeFileSync6 } from "fs";
|
|
702
|
+
import { homedir } from "os";
|
|
703
|
+
import { join as join7 } from "path";
|
|
704
|
+
import { randomUUID } from "crypto";
|
|
705
|
+
import os from "os";
|
|
706
|
+
import { PostHog } from "posthog-node";
|
|
707
|
+
|
|
708
|
+
// src/nuvio-posthog-token.ts
|
|
709
|
+
var NUVIO_POSTHOG_TOKEN = "phc_CJnWrLU4hB4aA88DJrPnma2WBMQqVHxUMVvrsye3R6x2";
|
|
710
|
+
|
|
711
|
+
// src/telemetry.ts
|
|
712
|
+
var POSTHOG_HOST = "https://us.i.posthog.com";
|
|
713
|
+
function telemetryFilePath() {
|
|
714
|
+
return join7(homedir(), ".nuvio", "telemetry.json");
|
|
715
|
+
}
|
|
716
|
+
var FORBIDDEN_PROP_KEYS = /* @__PURE__ */ new Set([
|
|
717
|
+
"cwd",
|
|
718
|
+
"root",
|
|
719
|
+
"file",
|
|
720
|
+
"path",
|
|
721
|
+
"name",
|
|
722
|
+
"message",
|
|
723
|
+
"stack"
|
|
724
|
+
]);
|
|
725
|
+
var client = null;
|
|
726
|
+
var sessionAnonymousId = null;
|
|
727
|
+
function telemetryDebug(message, detail) {
|
|
728
|
+
if (process.env.NUVIO_TELEMETRY_DEBUG !== "1") return;
|
|
729
|
+
if (detail !== void 0) {
|
|
730
|
+
console.error(`[nuvio telemetry] ${message}`, detail);
|
|
731
|
+
return;
|
|
732
|
+
}
|
|
733
|
+
console.error(`[nuvio telemetry] ${message}`);
|
|
734
|
+
}
|
|
735
|
+
function isTelemetryEnabled() {
|
|
736
|
+
const flag = process.env.NUVIO_TELEMETRY;
|
|
737
|
+
if (flag === "0") return false;
|
|
738
|
+
if (flag?.toLowerCase() === "false") return false;
|
|
739
|
+
return true;
|
|
740
|
+
}
|
|
741
|
+
function posthogToken() {
|
|
742
|
+
return process.env.NUVIO_POSTHOG_TOKEN ?? NUVIO_POSTHOG_TOKEN;
|
|
743
|
+
}
|
|
744
|
+
function tokenIsConfigured(token) {
|
|
745
|
+
return Boolean(token && token.startsWith("phc_"));
|
|
746
|
+
}
|
|
747
|
+
function readOrCreateAnonymousId() {
|
|
748
|
+
if (sessionAnonymousId) return sessionAnonymousId;
|
|
749
|
+
try {
|
|
750
|
+
const raw = readFileSync10(telemetryFilePath(), "utf8");
|
|
751
|
+
const parsed = JSON.parse(raw);
|
|
752
|
+
if (parsed.anonymousId) {
|
|
753
|
+
sessionAnonymousId = parsed.anonymousId;
|
|
754
|
+
return parsed.anonymousId;
|
|
755
|
+
}
|
|
756
|
+
} catch {
|
|
757
|
+
}
|
|
758
|
+
const id = randomUUID();
|
|
759
|
+
sessionAnonymousId = id;
|
|
760
|
+
try {
|
|
761
|
+
mkdirSync2(join7(homedir(), ".nuvio"), { recursive: true, mode: 448 });
|
|
762
|
+
writeFileSync6(
|
|
763
|
+
telemetryFilePath(),
|
|
764
|
+
JSON.stringify({ anonymousId: id }, null, 2),
|
|
765
|
+
{ mode: 384 }
|
|
766
|
+
);
|
|
767
|
+
} catch {
|
|
768
|
+
}
|
|
769
|
+
return id;
|
|
770
|
+
}
|
|
771
|
+
function getClient() {
|
|
772
|
+
if (!isTelemetryEnabled()) return null;
|
|
773
|
+
const token = posthogToken();
|
|
774
|
+
if (!tokenIsConfigured(token)) return null;
|
|
775
|
+
if (!client) {
|
|
776
|
+
client = new PostHog(token, {
|
|
777
|
+
host: POSTHOG_HOST,
|
|
778
|
+
flushAt: 1,
|
|
779
|
+
flushInterval: 0
|
|
780
|
+
});
|
|
781
|
+
telemetryDebug("PostHog client initialized", {
|
|
782
|
+
host: POSTHOG_HOST,
|
|
783
|
+
tokenPrefix: `${token.slice(0, 8)}\u2026`
|
|
784
|
+
});
|
|
785
|
+
}
|
|
786
|
+
return client;
|
|
787
|
+
}
|
|
788
|
+
function sanitizeProps(props) {
|
|
789
|
+
if (!props) return void 0;
|
|
790
|
+
const out = {};
|
|
791
|
+
for (const [key, value] of Object.entries(props)) {
|
|
792
|
+
if (FORBIDDEN_PROP_KEYS.has(key)) continue;
|
|
793
|
+
if (value === void 0) continue;
|
|
794
|
+
if (typeof value === "string" && /[/\\]/.test(value)) continue;
|
|
795
|
+
out[key] = value;
|
|
796
|
+
}
|
|
797
|
+
return Object.keys(out).length > 0 ? out : void 0;
|
|
798
|
+
}
|
|
799
|
+
function buildCliTelemetryProps(pm, project) {
|
|
800
|
+
const props = {
|
|
801
|
+
nuvio_version: NUVIO_VERSION,
|
|
802
|
+
os: process.platform,
|
|
803
|
+
arch: os.arch(),
|
|
804
|
+
node: process.version
|
|
805
|
+
};
|
|
806
|
+
if (pm) props.package_manager = pm;
|
|
807
|
+
if (project) {
|
|
808
|
+
props.has_react = true;
|
|
809
|
+
props.has_vite = true;
|
|
810
|
+
props.has_tailwind = project.tailwindOk;
|
|
811
|
+
}
|
|
812
|
+
return props;
|
|
813
|
+
}
|
|
814
|
+
function preflightErrorCode(message) {
|
|
815
|
+
if (message === MSG.noPackageJson) return "preflight_no_package_json";
|
|
816
|
+
if (message === MSG.noVite) return "preflight_no_vite";
|
|
817
|
+
if (message === MSG.noReact) return "preflight_no_react";
|
|
818
|
+
if (message === MSG.noViteDep) return "preflight_no_vite_dep";
|
|
819
|
+
if (message === MSG.monorepoRoot || message === MSG.cliPackage) {
|
|
820
|
+
return "preflight_monorepo";
|
|
821
|
+
}
|
|
822
|
+
return "preflight_unknown";
|
|
823
|
+
}
|
|
824
|
+
function captureCliEvent(event, props) {
|
|
825
|
+
try {
|
|
826
|
+
if (!isTelemetryEnabled()) {
|
|
827
|
+
telemetryDebug(`skipped ${event} (telemetry disabled)`);
|
|
828
|
+
return;
|
|
829
|
+
}
|
|
830
|
+
const ph = getClient();
|
|
831
|
+
if (!ph) {
|
|
832
|
+
telemetryDebug(`skipped ${event} (no PostHog client \u2014 check token)`);
|
|
833
|
+
return;
|
|
834
|
+
}
|
|
835
|
+
const distinctId = readOrCreateAnonymousId();
|
|
836
|
+
ph.capture({
|
|
837
|
+
distinctId,
|
|
838
|
+
event,
|
|
839
|
+
properties: sanitizeProps(props)
|
|
840
|
+
});
|
|
841
|
+
telemetryDebug(`captured ${event}`, { distinctId });
|
|
842
|
+
} catch (error) {
|
|
843
|
+
telemetryDebug(`capture failed for ${event}`, error);
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
async function shutdownTelemetry() {
|
|
847
|
+
try {
|
|
848
|
+
if (client) {
|
|
849
|
+
await client.flush();
|
|
850
|
+
await client.shutdown();
|
|
851
|
+
client = null;
|
|
852
|
+
telemetryDebug("flush + shutdown complete");
|
|
853
|
+
}
|
|
854
|
+
} catch (error) {
|
|
855
|
+
telemetryDebug("shutdown failed", error);
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
|
|
695
859
|
// src/init.ts
|
|
696
860
|
function isAutoYes(opts) {
|
|
697
861
|
if (opts.yes) return true;
|
|
@@ -756,28 +920,53 @@ ${MSG.partialHelp}`);
|
|
|
756
920
|
"\nNuvio helped you as far as it safely could. See warnings above."
|
|
757
921
|
);
|
|
758
922
|
}
|
|
923
|
+
console.log(`
|
|
924
|
+
${MSG.telemetryNotice}`);
|
|
759
925
|
}
|
|
760
926
|
async function runInit(opts) {
|
|
761
927
|
const root = opts.cwd;
|
|
928
|
+
const pm = detectPackageManager(root, opts.pm);
|
|
929
|
+
if (!opts.dryRun) {
|
|
930
|
+
captureCliEvent(
|
|
931
|
+
"nuvio_init_started",
|
|
932
|
+
buildCliTelemetryProps(pm)
|
|
933
|
+
);
|
|
934
|
+
}
|
|
762
935
|
let project;
|
|
763
936
|
try {
|
|
764
937
|
project = detectProject(root);
|
|
765
938
|
} catch (e) {
|
|
766
939
|
if (e instanceof PreflightError) {
|
|
767
940
|
console.error(e.message);
|
|
941
|
+
if (!opts.dryRun) {
|
|
942
|
+
captureCliEvent("nuvio_init_failed", {
|
|
943
|
+
...buildCliTelemetryProps(pm),
|
|
944
|
+
error_code: preflightErrorCode(e.message)
|
|
945
|
+
});
|
|
946
|
+
}
|
|
768
947
|
return 1;
|
|
769
948
|
}
|
|
770
949
|
if (opts.verbose) console.error(e);
|
|
771
950
|
console.error("Something went wrong. Run with --verbose for details.");
|
|
951
|
+
if (!opts.dryRun) {
|
|
952
|
+
captureCliEvent("nuvio_init_failed", {
|
|
953
|
+
...buildCliTelemetryProps(pm),
|
|
954
|
+
error_code: "unexpected_error"
|
|
955
|
+
});
|
|
956
|
+
}
|
|
772
957
|
return 2;
|
|
773
958
|
}
|
|
774
|
-
const
|
|
959
|
+
const projectProps = buildCliTelemetryProps(pm, project);
|
|
775
960
|
const plan = createPlan(root, pm);
|
|
776
961
|
plan.installCommand = opts.noInstall ? "(skipped \u2014 --no-install)" : installCommand(pm, NUVIO_VERSION);
|
|
777
962
|
if (project.tailwindWarn && !opts.skipTailwindCheck) {
|
|
778
963
|
const msg = "Tailwind CSS not detected. Class/style edits may not work until Tailwind is installed.";
|
|
779
964
|
if (opts.strict) {
|
|
780
965
|
console.error(MSG.strictTailwind);
|
|
966
|
+
captureCliEvent("nuvio_init_failed", {
|
|
967
|
+
...projectProps,
|
|
968
|
+
error_code: "strict_tailwind"
|
|
969
|
+
});
|
|
781
970
|
return 1;
|
|
782
971
|
}
|
|
783
972
|
plan.warnings.push(msg);
|
|
@@ -808,6 +997,10 @@ async function runInit(opts) {
|
|
|
808
997
|
const ok = await confirm(plan);
|
|
809
998
|
if (!ok) {
|
|
810
999
|
console.log("Cancelled.");
|
|
1000
|
+
captureCliEvent("nuvio_init_failed", {
|
|
1001
|
+
...projectProps,
|
|
1002
|
+
error_code: "user_cancelled"
|
|
1003
|
+
});
|
|
811
1004
|
return 1;
|
|
812
1005
|
}
|
|
813
1006
|
}
|
|
@@ -817,6 +1010,10 @@ async function runInit(opts) {
|
|
|
817
1010
|
const result = runInstall(root, pm, NUVIO_VERSION);
|
|
818
1011
|
if (!result.ok) {
|
|
819
1012
|
console.error(result.message ?? "Install failed.");
|
|
1013
|
+
captureCliEvent("nuvio_init_failed", {
|
|
1014
|
+
...projectProps,
|
|
1015
|
+
error_code: "install_failed"
|
|
1016
|
+
});
|
|
820
1017
|
return 1;
|
|
821
1018
|
}
|
|
822
1019
|
} else {
|
|
@@ -889,7 +1086,18 @@ async function runInit(opts) {
|
|
|
889
1086
|
project.viteConfigPath
|
|
890
1087
|
);
|
|
891
1088
|
printVerification(verification);
|
|
892
|
-
if (plan.tier === "failed")
|
|
1089
|
+
if (plan.tier === "failed") {
|
|
1090
|
+
captureCliEvent("nuvio_init_failed", {
|
|
1091
|
+
...projectProps,
|
|
1092
|
+
error_code: "init_tier_failed",
|
|
1093
|
+
result_tier: "failed"
|
|
1094
|
+
});
|
|
1095
|
+
return 1;
|
|
1096
|
+
}
|
|
1097
|
+
captureCliEvent("nuvio_init_completed", {
|
|
1098
|
+
...projectProps,
|
|
1099
|
+
result_tier: plan.tier
|
|
1100
|
+
});
|
|
893
1101
|
return plan.tier === "partial" || plan.tier === "full" ? 0 : 1;
|
|
894
1102
|
}
|
|
895
1103
|
|
|
@@ -968,9 +1176,16 @@ async function runCli(argv) {
|
|
|
968
1176
|
try {
|
|
969
1177
|
return await runInit(opts);
|
|
970
1178
|
} catch (e) {
|
|
1179
|
+
const pm = detectPackageManager(opts.cwd, opts.pm);
|
|
1180
|
+
captureCliEvent("nuvio_init_failed", {
|
|
1181
|
+
...buildCliTelemetryProps(pm),
|
|
1182
|
+
error_code: "unexpected_error"
|
|
1183
|
+
});
|
|
971
1184
|
if (opts.verbose) console.error(e);
|
|
972
1185
|
else console.error("Something went wrong. Run with --verbose for details.");
|
|
973
1186
|
return 2;
|
|
1187
|
+
} finally {
|
|
1188
|
+
await shutdownTelemetry();
|
|
974
1189
|
}
|
|
975
1190
|
}
|
|
976
1191
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nuvio/cli",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.4",
|
|
4
4
|
"description": "Nuvio CLI: one-command Vite + React onboarding (nuvio init).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -36,7 +36,8 @@
|
|
|
36
36
|
"@babel/parser": "^7.26.9",
|
|
37
37
|
"@babel/traverse": "^7.26.9",
|
|
38
38
|
"@babel/types": "^7.26.9",
|
|
39
|
-
"fast-glob": "^3.3.3"
|
|
39
|
+
"fast-glob": "^3.3.3",
|
|
40
|
+
"posthog-node": "^5.36.2"
|
|
40
41
|
},
|
|
41
42
|
"devDependencies": {
|
|
42
43
|
"@types/babel__generator": "^7.6.8",
|