@apso/cli 0.20.0 → 0.22.0

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.
@@ -0,0 +1,162 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.__testing = exports.shutdownTelemetry = exports.elapsedMs = exports.captureException = exports.track = exports.initTelemetry = void 0;
4
+ const tslib_1 = require("tslib");
5
+ /**
6
+ * CLI telemetry — usage analytics (PostHog) + error reporting (Sentry).
7
+ *
8
+ * Wired to the same Apso systems the web app and build engine use so CLI usage
9
+ * shows up in the same funnels. Fully opt-out:
10
+ * - `apso config set telemetryDisabled true`
11
+ * - env DO_NOT_TRACK=1 (honors the console.dev standard)
12
+ * - env APSO_TELEMETRY_DISABLED=1
13
+ *
14
+ * Everything here is best-effort and MUST NOT throw or block a command. All
15
+ * public functions swallow their own errors. The PostHog project keys and the
16
+ * Sentry DSN are public client ingestion keys (the same class of token shipped
17
+ * to browsers), safe to bundle.
18
+ */
19
+ const posthog_node_1 = require("posthog-node");
20
+ const Sentry = tslib_1.__importStar(require("@sentry/node"));
21
+ const crypto_1 = require("crypto");
22
+ const os_1 = tslib_1.__importDefault(require("os"));
23
+ const config_1 = require("../config");
24
+ // Public ingestion keys (same tokens the browser/build-engine ship, per env).
25
+ const POSTHOG_KEY_PROD = "phc_Es9UUMw8pOs7Bs21b5PhfC5Qdo94GpYVv8MfdrG12LC";
26
+ const POSTHOG_KEY_STAGING = "phc_bkc33v3wo5LRvgPF6IdjsgDEDOwgJMz95MR2YIlvELk";
27
+ const POSTHOG_HOST = "https://us.i.posthog.com";
28
+ // Dedicated apsoai/apso-cli Sentry project DSN (public client key).
29
+ const SENTRY_DSN_DEFAULT = "https://754ae10322af78af3edbb87b93383ebb@o4509208616435714.ingest.us.sentry.io/4511796852162560";
30
+ let cliVersion = "0.0.0";
31
+ try {
32
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
33
+ cliVersion = require("../../../package.json").version || cliVersion;
34
+ }
35
+ catch {
36
+ // ignore
37
+ }
38
+ let posthog = null;
39
+ let enabled = false;
40
+ let sentryOn = false;
41
+ let distinctId = "cli-anonymous";
42
+ let environment = "production";
43
+ let startedAt = Date.now();
44
+ function isDisabled(telemetryDisabled) {
45
+ if (process.env.DO_NOT_TRACK === "1" || process.env.DO_NOT_TRACK === "true")
46
+ return true;
47
+ if (process.env.APSO_TELEMETRY_DISABLED === "1")
48
+ return true;
49
+ return !!telemetryDisabled;
50
+ }
51
+ /** Stable, non-PII fallback id for logged-out users (no username, no MAC). */
52
+ function anonMachineId() {
53
+ const seed = `${os_1.default.hostname()}|${os_1.default.platform()}|${os_1.default.arch()}`;
54
+ return "cli_" + (0, crypto_1.createHash)("sha256").update(seed).digest("hex").slice(0, 20);
55
+ }
56
+ /**
57
+ * Initialize telemetry. Call once at process start (init hook). Safe to call
58
+ * when disabled — it just no-ops everything downstream.
59
+ */
60
+ function initTelemetry() {
61
+ var _a, _b, _c;
62
+ try {
63
+ startedAt = Date.now();
64
+ const cfg = config_1.globalConfig.read();
65
+ if (isDisabled(cfg.telemetryDisabled)) {
66
+ enabled = false;
67
+ return;
68
+ }
69
+ enabled = true;
70
+ environment = (cfg.apiUrl || "").includes("staging")
71
+ ? "staging"
72
+ : "production";
73
+ const creds = config_1.credentials.read();
74
+ distinctId = ((_a = creds === null || creds === void 0 ? void 0 : creds.user) === null || _a === void 0 ? void 0 : _a.id) || anonMachineId();
75
+ posthog = new posthog_node_1.PostHog(environment === "staging" ? POSTHOG_KEY_STAGING : POSTHOG_KEY_PROD, { host: POSTHOG_HOST, flushAt: 1, flushInterval: 0 });
76
+ if ((_b = creds === null || creds === void 0 ? void 0 : creds.user) === null || _b === void 0 ? void 0 : _b.email) {
77
+ // Attach identity so CLI events attribute to the same person as the app.
78
+ posthog.identify({
79
+ distinctId,
80
+ properties: { email: creds.user.email, name: creds.user.name },
81
+ });
82
+ }
83
+ const dsn = process.env.APSO_CLI_SENTRY_DSN || SENTRY_DSN_DEFAULT;
84
+ if (dsn) {
85
+ Sentry.init({
86
+ dsn,
87
+ environment,
88
+ release: `apso-cli@${cliVersion}`,
89
+ tracesSampleRate: 0,
90
+ });
91
+ Sentry.setUser({ id: distinctId, email: (_c = creds === null || creds === void 0 ? void 0 : creds.user) === null || _c === void 0 ? void 0 : _c.email });
92
+ Sentry.setTag("cli_version", cliVersion);
93
+ sentryOn = true;
94
+ }
95
+ }
96
+ catch {
97
+ enabled = false;
98
+ }
99
+ }
100
+ exports.initTelemetry = initTelemetry;
101
+ function commonProps() {
102
+ return {
103
+ cli_version: cliVersion,
104
+ environment,
105
+ os: os_1.default.platform(),
106
+ arch: os_1.default.arch(),
107
+ node_version: process.version,
108
+ authenticated: distinctId !== anonMachineId() && !distinctId.startsWith("cli_"),
109
+ };
110
+ }
111
+ /** Emit a PostHog event. No-op when disabled. */
112
+ function track(event, properties = {}) {
113
+ try {
114
+ if (!enabled || !posthog)
115
+ return;
116
+ posthog.capture({
117
+ distinctId,
118
+ event,
119
+ properties: { ...commonProps(), ...properties },
120
+ });
121
+ }
122
+ catch {
123
+ // never break a command over telemetry
124
+ }
125
+ }
126
+ exports.track = track;
127
+ /** Report an exception to Sentry. No-op when disabled. */
128
+ function captureException(err, context = {}) {
129
+ try {
130
+ if (!enabled || !sentryOn)
131
+ return;
132
+ Sentry.captureException(err, { extra: context });
133
+ }
134
+ catch {
135
+ // ignore
136
+ }
137
+ }
138
+ exports.captureException = captureException;
139
+ /** Milliseconds since the process (CLI invocation) started. */
140
+ function elapsedMs() {
141
+ return Date.now() - startedAt;
142
+ }
143
+ exports.elapsedMs = elapsedMs;
144
+ /** Flush + close both clients. Call before the process exits. */
145
+ async function shutdownTelemetry() {
146
+ try {
147
+ if (posthog)
148
+ await posthog.shutdown();
149
+ }
150
+ catch {
151
+ // ignore
152
+ }
153
+ try {
154
+ if (sentryOn)
155
+ await Sentry.close(2000);
156
+ }
157
+ catch {
158
+ // ignore
159
+ }
160
+ }
161
+ exports.shutdownTelemetry = shutdownTelemetry;
162
+ exports.__testing = { isDisabled, anonMachineId };
@@ -1,3 +1,4 @@
1
- // Code generated by APSO CLI. DO NOT EDIT.
2
- // This file is auto-generated - any changes will be overwritten.
3
- // Generated at: <%= new Date().toISOString() %>
1
+ // Code generated by Apso CLI. DO NOT EDIT.
2
+ // Generated with Apso (https://apso.ai)
3
+ //
4
+ // This file is auto-generated; changes will be overwritten on the next scaffold.
@@ -1,8 +1,6 @@
1
- <% /*
2
- * This file is generated by Apso.
1
+ /**
2
+ * Generated with Apso (https://apso.ai)
3
3
  *
4
- * Generated at: <%= it.generatedAt %>
5
- * Generated by: <%= it.generatedBy %>
6
- *
7
- * Do not edit this file directly.
8
- */ %>
4
+ * This file is auto-generated by the Apso CLI. Do not edit it directly;
5
+ * changes will be overwritten on the next scaffold.
6
+ */
@@ -1,6 +1,6 @@
1
1
  # -*- coding: utf-8 -*-
2
- # Generated: <%= it.generatedAt %>
3
-
4
- # Auto-generated by Apso CLI
5
- # DO NOT EDIT - This file is managed by Apso and will be overwritten on scaffold
2
+ # Generated with Apso (https://apso.ai)
3
+ #
4
+ # This file is auto-generated by the Apso CLI. Do not edit it directly;
5
+ # changes will be overwritten on the next scaffold.
6
6