@wraps.dev/cli 1.2.0 → 1.3.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.
package/dist/cli.js CHANGED
@@ -4,6 +4,9 @@ var __getOwnPropNames = Object.getOwnPropertyNames;
4
4
  var __esm = (fn, res) => function __init() {
5
5
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
6
6
  };
7
+ var __commonJS = (cb, mod) => function __require() {
8
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
9
+ };
7
10
  var __export = (target, all3) => {
8
11
  for (var name in all3)
9
12
  __defProp(target, name, { get: all3[name], enumerable: true });
@@ -18,12 +21,456 @@ var init_esm_shims = __esm({
18
21
  }
19
22
  });
20
23
 
24
+ // src/utils/shared/ci-detection.ts
25
+ function isCI() {
26
+ if (process.env.CI === "true" || process.env.CI === "1") {
27
+ return true;
28
+ }
29
+ const ciEnvVars = [
30
+ "GITHUB_ACTIONS",
31
+ // GitHub Actions
32
+ "GITLAB_CI",
33
+ // GitLab CI
34
+ "CIRCLECI",
35
+ // CircleCI
36
+ "TRAVIS",
37
+ // Travis CI
38
+ "JENKINS_URL",
39
+ // Jenkins
40
+ "BUILDKITE",
41
+ // Buildkite
42
+ "DRONE",
43
+ // Drone
44
+ "SEMAPHORE",
45
+ // Semaphore
46
+ "TEAMCITY_VERSION",
47
+ // TeamCity
48
+ "TF_BUILD",
49
+ // Azure Pipelines
50
+ "CODEBUILD_BUILD_ID",
51
+ // AWS CodeBuild
52
+ "NETLIFY",
53
+ // Netlify
54
+ "VERCEL",
55
+ // Vercel
56
+ "HEROKU_TEST_RUN_ID",
57
+ // Heroku CI
58
+ "BUDDY",
59
+ // Buddy
60
+ "BITBUCKET_BUILD_NUMBER"
61
+ // Bitbucket Pipelines
62
+ ];
63
+ return ciEnvVars.some((envVar) => process.env[envVar] !== void 0);
64
+ }
65
+ var init_ci_detection = __esm({
66
+ "src/utils/shared/ci-detection.ts"() {
67
+ "use strict";
68
+ init_esm_shims();
69
+ }
70
+ });
71
+
72
+ // src/telemetry/config.ts
73
+ import Conf from "conf";
74
+ import { v4 as uuidv4 } from "uuid";
75
+ var CONFIG_DEFAULTS, TelemetryConfigManager;
76
+ var init_config = __esm({
77
+ "src/telemetry/config.ts"() {
78
+ "use strict";
79
+ init_esm_shims();
80
+ CONFIG_DEFAULTS = {
81
+ enabled: true,
82
+ anonymousId: uuidv4(),
83
+ notificationShown: false
84
+ };
85
+ TelemetryConfigManager = class {
86
+ config;
87
+ constructor() {
88
+ this.config = new Conf({
89
+ projectName: "wraps",
90
+ configName: "telemetry",
91
+ defaults: CONFIG_DEFAULTS
92
+ });
93
+ }
94
+ /**
95
+ * Check if telemetry is enabled
96
+ */
97
+ isEnabled() {
98
+ return this.config.get("enabled");
99
+ }
100
+ /**
101
+ * Enable or disable telemetry
102
+ */
103
+ setEnabled(enabled) {
104
+ this.config.set("enabled", enabled);
105
+ }
106
+ /**
107
+ * Get the anonymous user ID
108
+ */
109
+ getAnonymousId() {
110
+ return this.config.get("anonymousId");
111
+ }
112
+ /**
113
+ * Check if the first-run notification has been shown
114
+ */
115
+ hasShownNotification() {
116
+ return this.config.get("notificationShown");
117
+ }
118
+ /**
119
+ * Mark the first-run notification as shown
120
+ */
121
+ markNotificationShown() {
122
+ this.config.set("notificationShown", true);
123
+ }
124
+ /**
125
+ * Get the full path to the configuration file
126
+ */
127
+ getConfigPath() {
128
+ return this.config.path;
129
+ }
130
+ /**
131
+ * Reset configuration to defaults
132
+ */
133
+ reset() {
134
+ this.config.clear();
135
+ this.config.set({
136
+ ...CONFIG_DEFAULTS,
137
+ anonymousId: uuidv4()
138
+ });
139
+ }
140
+ };
141
+ }
142
+ });
143
+
144
+ // package.json
145
+ var require_package = __commonJS({
146
+ "package.json"(exports, module) {
147
+ module.exports = {
148
+ name: "@wraps.dev/cli",
149
+ version: "1.3.0",
150
+ description: "CLI for deploying Wraps email infrastructure to your AWS account",
151
+ type: "module",
152
+ main: "./dist/cli.js",
153
+ bin: {
154
+ wraps: "./dist/cli.js"
155
+ },
156
+ files: [
157
+ "dist",
158
+ "README.md",
159
+ "LICENSE"
160
+ ],
161
+ repository: {
162
+ type: "git",
163
+ url: "https://github.com/wraps-team/wraps.git",
164
+ directory: "packages/cli"
165
+ },
166
+ homepage: "https://wraps.dev",
167
+ bugs: {
168
+ url: "https://github.com/wraps-team/wraps/issues"
169
+ },
170
+ publishConfig: {
171
+ access: "public"
172
+ },
173
+ scripts: {
174
+ dev: "tsup --watch",
175
+ build: "pnpm build:console && pnpm build:lambda && tsup",
176
+ "build:lambda": "tsx scripts/build-lambda.ts",
177
+ "build:console": "pnpm --filter @wraps/console build",
178
+ test: "vitest run",
179
+ "test:watch": "vitest --watch",
180
+ "test:ui": "vitest --ui",
181
+ "test:coverage": "vitest run --coverage",
182
+ typecheck: "tsc --noEmit",
183
+ lint: "eslint src",
184
+ prepublishOnly: "pnpm build"
185
+ },
186
+ keywords: [
187
+ "aws",
188
+ "ses",
189
+ "email",
190
+ "infrastructure",
191
+ "cli"
192
+ ],
193
+ author: "Wraps",
194
+ license: "MIT",
195
+ dependencies: {
196
+ "@aws-sdk/client-acm": "3.933.0",
197
+ "@aws-sdk/client-cloudformation": "^3.490.0",
198
+ "@aws-sdk/client-cloudfront": "3.933.0",
199
+ "@aws-sdk/client-cloudwatch": "^3.490.0",
200
+ "@aws-sdk/client-dynamodb": "^3.490.0",
201
+ "@aws-sdk/client-iam": "3.932.0",
202
+ "@aws-sdk/client-lambda": "3.925.0",
203
+ "@aws-sdk/client-mailmanager": "3.925.0",
204
+ "@aws-sdk/client-route-53": "3.925.0",
205
+ "@aws-sdk/client-ses": "^3.490.0",
206
+ "@aws-sdk/client-sesv2": "3.925.0",
207
+ "@aws-sdk/client-sns": "^3.490.0",
208
+ "@aws-sdk/client-sts": "^3.490.0",
209
+ "@aws-sdk/util-dynamodb": "3.927.0",
210
+ "@clack/prompts": "^0.11.0",
211
+ "@pulumi/aws": "^7.11.1",
212
+ "@pulumi/pulumi": "^3.207.0",
213
+ args: "^5.0.3",
214
+ conf: "^13.0.1",
215
+ cosmiconfig: "^9.0.0",
216
+ esbuild: "^0.25.12",
217
+ express: "^4.21.2",
218
+ "get-port": "^7.1.0",
219
+ "http-terminator": "^3.2.0",
220
+ "isomorphic-dompurify": "2.32.0",
221
+ mailparser: "3.9.0",
222
+ open: "^10.1.0",
223
+ picocolors: "^1.1.1",
224
+ tabtab: "^3.0.2",
225
+ uuid: "^11.0.3"
226
+ },
227
+ devDependencies: {
228
+ "@types/args": "5.0.4",
229
+ "@types/express": "^5.0.0",
230
+ "@types/mailparser": "3.4.6",
231
+ "@types/node": "^20.11.0",
232
+ "@types/uuid": "^10.0.0",
233
+ "@vitest/coverage-v8": "4.0.7",
234
+ "aws-sdk-client-mock": "4.1.0",
235
+ "aws-sdk-client-mock-vitest": "7.0.1",
236
+ eslint: "^8.56.0",
237
+ tsup: "^8.0.1",
238
+ tsx: "4.20.6",
239
+ typescript: "catalog:",
240
+ vitest: "^4.0.7"
241
+ },
242
+ engines: {
243
+ node: ">=20.0.0"
244
+ }
245
+ };
246
+ }
247
+ });
248
+
249
+ // src/telemetry/client.ts
250
+ function getTelemetryClient() {
251
+ if (!telemetryInstance) {
252
+ telemetryInstance = new TelemetryClient();
253
+ }
254
+ return telemetryInstance;
255
+ }
256
+ var DEFAULT_ENDPOINT, DEFAULT_TIMEOUT, TelemetryClient, telemetryInstance;
257
+ var init_client = __esm({
258
+ "src/telemetry/client.ts"() {
259
+ "use strict";
260
+ init_esm_shims();
261
+ init_ci_detection();
262
+ init_config();
263
+ DEFAULT_ENDPOINT = "https://wraps.dev/api/telemetry";
264
+ DEFAULT_TIMEOUT = 2e3;
265
+ TelemetryClient = class {
266
+ config;
267
+ endpoint;
268
+ timeout;
269
+ debug;
270
+ enabled;
271
+ eventQueue = [];
272
+ flushTimer;
273
+ constructor(options = {}) {
274
+ this.config = new TelemetryConfigManager();
275
+ this.endpoint = options.endpoint || DEFAULT_ENDPOINT;
276
+ this.timeout = options.timeout || DEFAULT_TIMEOUT;
277
+ this.debug = options.debug || process.env.WRAPS_TELEMETRY_DEBUG === "1";
278
+ this.enabled = this.shouldBeEnabled();
279
+ }
280
+ /**
281
+ * Determine if telemetry should be enabled based on environment and config
282
+ */
283
+ shouldBeEnabled() {
284
+ if (process.env.DO_NOT_TRACK === "1") {
285
+ return false;
286
+ }
287
+ if (process.env.WRAPS_TELEMETRY_DISABLED === "1") {
288
+ return false;
289
+ }
290
+ if (isCI()) {
291
+ return false;
292
+ }
293
+ if (!this.config.isEnabled()) {
294
+ return false;
295
+ }
296
+ return true;
297
+ }
298
+ /**
299
+ * Track an event
300
+ *
301
+ * @param event - Event name in format "category:action" (e.g., "command:init")
302
+ * @param properties - Additional event properties (no PII)
303
+ */
304
+ track(event, properties) {
305
+ const telemetryEvent = {
306
+ event,
307
+ properties: {
308
+ ...properties,
309
+ cli_version: this.getCLIVersion(),
310
+ os: process.platform,
311
+ node_version: process.version,
312
+ ci: isCI()
313
+ },
314
+ anonymousId: this.config.getAnonymousId(),
315
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
316
+ };
317
+ if (this.debug) {
318
+ console.log(
319
+ "[Telemetry Debug] Event:",
320
+ JSON.stringify(telemetryEvent, null, 2)
321
+ );
322
+ return;
323
+ }
324
+ if (!this.enabled) {
325
+ return;
326
+ }
327
+ this.eventQueue.push(telemetryEvent);
328
+ if (this.flushTimer) {
329
+ clearTimeout(this.flushTimer);
330
+ }
331
+ this.flushTimer = setTimeout(() => this.flush(), 100);
332
+ }
333
+ /**
334
+ * Flush queued events to server
335
+ */
336
+ async flush() {
337
+ if (this.eventQueue.length === 0) {
338
+ return;
339
+ }
340
+ const eventsToSend = [...this.eventQueue];
341
+ this.eventQueue = [];
342
+ try {
343
+ const controller = new AbortController();
344
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
345
+ const requestBody = {
346
+ events: eventsToSend,
347
+ batch: true
348
+ };
349
+ await fetch(this.endpoint, {
350
+ method: "POST",
351
+ headers: {
352
+ "Content-Type": "application/json"
353
+ },
354
+ body: JSON.stringify(requestBody),
355
+ signal: controller.signal
356
+ });
357
+ clearTimeout(timeoutId);
358
+ } catch (error) {
359
+ if (this.debug) {
360
+ console.error("[Telemetry Debug] Failed to send events:", error);
361
+ }
362
+ }
363
+ }
364
+ /**
365
+ * Flush and wait for all events to be sent
366
+ * Should be called before CLI exits
367
+ */
368
+ async shutdown() {
369
+ if (this.flushTimer) {
370
+ clearTimeout(this.flushTimer);
371
+ }
372
+ await this.flush();
373
+ }
374
+ /**
375
+ * Enable telemetry
376
+ * Note: Won't override environment variable opt-outs (DO_NOT_TRACK, CI, etc.)
377
+ */
378
+ enable() {
379
+ this.config.setEnabled(true);
380
+ if (process.env.DO_NOT_TRACK === "1" || process.env.DO_NOT_TRACK === "true") {
381
+ this.enabled = false;
382
+ return;
383
+ }
384
+ if (process.env.WRAPS_TELEMETRY_DISABLED === "1") {
385
+ this.enabled = false;
386
+ return;
387
+ }
388
+ if (isCI()) {
389
+ this.enabled = false;
390
+ return;
391
+ }
392
+ this.enabled = true;
393
+ }
394
+ /**
395
+ * Disable telemetry
396
+ */
397
+ disable() {
398
+ this.config.setEnabled(false);
399
+ this.enabled = false;
400
+ this.eventQueue = [];
401
+ }
402
+ /**
403
+ * Check if telemetry is enabled
404
+ */
405
+ isEnabled() {
406
+ return this.enabled;
407
+ }
408
+ /**
409
+ * Get config file path
410
+ */
411
+ getConfigPath() {
412
+ return this.config.getConfigPath();
413
+ }
414
+ /**
415
+ * Show first-run notification
416
+ */
417
+ shouldShowNotification() {
418
+ return this.enabled && !this.config.hasShownNotification();
419
+ }
420
+ /**
421
+ * Mark notification as shown
422
+ */
423
+ markNotificationShown() {
424
+ this.config.markNotificationShown();
425
+ }
426
+ /**
427
+ * Get CLI version from package.json
428
+ */
429
+ getCLIVersion() {
430
+ try {
431
+ const packageJson2 = require_package();
432
+ return packageJson2.version;
433
+ } catch {
434
+ return "unknown";
435
+ }
436
+ }
437
+ };
438
+ telemetryInstance = null;
439
+ }
440
+ });
441
+
442
+ // src/telemetry/events.ts
443
+ function trackCommand(command, metadata) {
444
+ const client = getTelemetryClient();
445
+ const sanitized = metadata ? { ...metadata } : {};
446
+ sanitized.domain = void 0;
447
+ sanitized.accountId = void 0;
448
+ sanitized.email = void 0;
449
+ client.track(`command:${command}`, sanitized);
450
+ }
451
+ function trackError(errorCode, command, metadata) {
452
+ const client = getTelemetryClient();
453
+ client.track("error:occurred", {
454
+ error_code: errorCode,
455
+ command,
456
+ ...metadata
457
+ });
458
+ }
459
+ var init_events = __esm({
460
+ "src/telemetry/events.ts"() {
461
+ "use strict";
462
+ init_esm_shims();
463
+ init_client();
464
+ }
465
+ });
466
+
21
467
  // src/utils/shared/errors.ts
22
468
  import * as clack from "@clack/prompts";
23
469
  import pc from "picocolors";
24
470
  function handleCLIError(error) {
25
471
  console.error("");
26
472
  if (error instanceof WrapsError) {
473
+ trackError(error.code, "unknown");
27
474
  clack.log.error(error.message);
28
475
  if (error.suggestion) {
29
476
  console.log(`
@@ -38,6 +485,7 @@ ${pc.yellow("Suggestion:")}`);
38
485
  }
39
486
  process.exit(1);
40
487
  }
488
+ trackError("UNKNOWN_ERROR", "unknown");
41
489
  clack.log.error("An unexpected error occurred");
42
490
  console.error(error);
43
491
  console.log(`
@@ -51,6 +499,7 @@ var init_errors = __esm({
51
499
  "src/utils/shared/errors.ts"() {
52
500
  "use strict";
53
501
  init_esm_shims();
502
+ init_events();
54
503
  WrapsError = class extends Error {
55
504
  constructor(message, code, suggestion, docsUrl) {
56
505
  super(message);
@@ -2206,9 +2655,9 @@ init_esm_shims();
2206
2655
  import { readFileSync } from "fs";
2207
2656
  import { dirname as dirname2, join as join4 } from "path";
2208
2657
  import { fileURLToPath as fileURLToPath4 } from "url";
2209
- import * as clack13 from "@clack/prompts";
2658
+ import * as clack14 from "@clack/prompts";
2210
2659
  import args from "args";
2211
- import pc14 from "picocolors";
2660
+ import pc15 from "picocolors";
2212
2661
 
2213
2662
  // src/commands/dashboard/update-role.ts
2214
2663
  init_esm_shims();
@@ -7548,6 +7997,67 @@ Run ${pc13.cyan("wraps email init")} to deploy infrastructure.
7548
7997
  });
7549
7998
  }
7550
7999
 
8000
+ // src/commands/telemetry.ts
8001
+ init_esm_shims();
8002
+ init_client();
8003
+ import * as clack13 from "@clack/prompts";
8004
+ import pc14 from "picocolors";
8005
+ async function telemetryEnable() {
8006
+ const client = getTelemetryClient();
8007
+ client.enable();
8008
+ clack13.log.success(pc14.green("Telemetry enabled"));
8009
+ console.log(` Config: ${pc14.dim(client.getConfigPath())}`);
8010
+ console.log(`
8011
+ ${pc14.dim("Thank you for helping improve Wraps!")}
8012
+ `);
8013
+ }
8014
+ async function telemetryDisable() {
8015
+ const client = getTelemetryClient();
8016
+ client.disable();
8017
+ clack13.log.success(pc14.green("Telemetry disabled"));
8018
+ console.log(` Config: ${pc14.dim(client.getConfigPath())}`);
8019
+ console.log(
8020
+ `
8021
+ ${pc14.dim("You can re-enable with:")} wraps telemetry enable
8022
+ `
8023
+ );
8024
+ }
8025
+ async function telemetryStatus() {
8026
+ const client = getTelemetryClient();
8027
+ clack13.intro(pc14.bold("Telemetry Status"));
8028
+ const status2 = client.isEnabled() ? pc14.green("Enabled") : pc14.red("Disabled");
8029
+ console.log();
8030
+ console.log(` ${pc14.bold("Status:")} ${status2}`);
8031
+ console.log(` ${pc14.bold("Config file:")} ${pc14.dim(client.getConfigPath())}`);
8032
+ if (client.isEnabled()) {
8033
+ console.log();
8034
+ console.log(pc14.bold(" How to opt-out:"));
8035
+ console.log(` ${pc14.cyan("wraps telemetry disable")}`);
8036
+ console.log(
8037
+ ` ${pc14.dim("Or set:")} ${pc14.cyan("WRAPS_TELEMETRY_DISABLED=1")}`
8038
+ );
8039
+ console.log(` ${pc14.dim("Or set:")} ${pc14.cyan("DO_NOT_TRACK=1")}`);
8040
+ } else {
8041
+ console.log();
8042
+ console.log(pc14.bold(" How to opt-in:"));
8043
+ console.log(` ${pc14.cyan("wraps telemetry enable")}`);
8044
+ }
8045
+ console.log();
8046
+ console.log(pc14.bold(" Debug mode:"));
8047
+ console.log(
8048
+ ` ${pc14.dim("See what would be sent:")} ${pc14.cyan("WRAPS_TELEMETRY_DEBUG=1 wraps <command>")}`
8049
+ );
8050
+ console.log();
8051
+ console.log(
8052
+ ` ${pc14.dim("Learn more:")} ${pc14.cyan("https://wraps.dev/docs/telemetry")}`
8053
+ );
8054
+ console.log();
8055
+ }
8056
+
8057
+ // src/cli.ts
8058
+ init_client();
8059
+ init_events();
8060
+
7551
8061
  // src/utils/shared/completion.ts
7552
8062
  init_esm_shims();
7553
8063
  function setupTabCompletion() {
@@ -7586,56 +8096,57 @@ function showVersion() {
7586
8096
  process.exit(0);
7587
8097
  }
7588
8098
  function showHelp() {
7589
- clack13.intro(pc14.bold(`WRAPS CLI v${VERSION}`));
8099
+ clack14.intro(pc15.bold(`WRAPS CLI v${VERSION}`));
7590
8100
  console.log("Deploy AWS infrastructure to your account\n");
7591
8101
  console.log("Usage: wraps [service] <command> [options]\n");
7592
8102
  console.log("Services:");
7593
- console.log(` ${pc14.cyan("email")} Email infrastructure (AWS SES)`);
8103
+ console.log(` ${pc15.cyan("email")} Email infrastructure (AWS SES)`);
7594
8104
  console.log(
7595
- ` ${pc14.cyan("sms")} SMS infrastructure (AWS End User Messaging) ${pc14.dim("[coming soon]")}
8105
+ ` ${pc15.cyan("sms")} SMS infrastructure (AWS End User Messaging) ${pc15.dim("[coming soon]")}
7596
8106
  `
7597
8107
  );
7598
8108
  console.log("Email Commands:");
7599
8109
  console.log(
7600
- ` ${pc14.cyan("email init")} Deploy new email infrastructure`
8110
+ ` ${pc15.cyan("email init")} Deploy new email infrastructure`
7601
8111
  );
7602
8112
  console.log(
7603
- ` ${pc14.cyan("email connect")} Connect to existing AWS SES`
8113
+ ` ${pc15.cyan("email connect")} Connect to existing AWS SES`
7604
8114
  );
7605
- console.log(` ${pc14.cyan("email domains verify")} Verify domain DNS records`);
7606
- console.log(` ${pc14.cyan("email config")} Update infrastructure`);
7607
- console.log(` ${pc14.cyan("email upgrade")} Add features`);
8115
+ console.log(` ${pc15.cyan("email domains verify")} Verify domain DNS records`);
8116
+ console.log(` ${pc15.cyan("email config")} Update infrastructure`);
8117
+ console.log(` ${pc15.cyan("email upgrade")} Add features`);
7608
8118
  console.log(
7609
- ` ${pc14.cyan("email restore")} Restore original configuration
8119
+ ` ${pc15.cyan("email restore")} Restore original configuration
7610
8120
  `
7611
8121
  );
7612
8122
  console.log("Console & Dashboard:");
7613
- console.log(` ${pc14.cyan("console")} Start local web console`);
8123
+ console.log(` ${pc15.cyan("console")} Start local web console`);
7614
8124
  console.log(
7615
- ` ${pc14.cyan("dashboard update-role")} Update hosted dashboard IAM permissions
8125
+ ` ${pc15.cyan("dashboard update-role")} Update hosted dashboard IAM permissions
7616
8126
  `
7617
8127
  );
7618
8128
  console.log("Global Commands:");
7619
- console.log(` ${pc14.cyan("status")} Show all infrastructure status`);
7620
- console.log(` ${pc14.cyan("destroy")} Remove deployed infrastructure`);
8129
+ console.log(` ${pc15.cyan("status")} Show all infrastructure status`);
8130
+ console.log(` ${pc15.cyan("destroy")} Remove deployed infrastructure`);
8131
+ console.log(` ${pc15.cyan("completion")} Generate shell completion script`);
7621
8132
  console.log(
7622
- ` ${pc14.cyan("completion")} Generate shell completion script
8133
+ ` ${pc15.cyan("telemetry")} Manage anonymous telemetry settings
7623
8134
  `
7624
8135
  );
7625
8136
  console.log("Options:");
7626
8137
  console.log(
7627
- ` ${pc14.dim("-p, --provider")} Hosting provider (vercel, aws, railway, other)`
8138
+ ` ${pc15.dim("-p, --provider")} Hosting provider (vercel, aws, railway, other)`
7628
8139
  );
7629
- console.log(` ${pc14.dim("-r, --region")} AWS region`);
7630
- console.log(` ${pc14.dim("-d, --domain")} Domain name`);
7631
- console.log(` ${pc14.dim("--account")} AWS account ID or alias`);
7632
- console.log(` ${pc14.dim("--preset")} Configuration preset`);
7633
- console.log(` ${pc14.dim("-y, --yes")} Skip confirmation prompts`);
7634
- console.log(` ${pc14.dim("-f, --force")} Force destructive operations`);
7635
- console.log(` ${pc14.dim("-v, --version")} Show version number
8140
+ console.log(` ${pc15.dim("-r, --region")} AWS region`);
8141
+ console.log(` ${pc15.dim("-d, --domain")} Domain name`);
8142
+ console.log(` ${pc15.dim("--account")} AWS account ID or alias`);
8143
+ console.log(` ${pc15.dim("--preset")} Configuration preset`);
8144
+ console.log(` ${pc15.dim("-y, --yes")} Skip confirmation prompts`);
8145
+ console.log(` ${pc15.dim("-f, --force")} Force destructive operations`);
8146
+ console.log(` ${pc15.dim("-v, --version")} Show version number
7636
8147
  `);
7637
8148
  console.log(
7638
- `Run ${pc14.cyan("wraps <service> <command> --help")} for more information.
8149
+ `Run ${pc15.cyan("wraps <service> <command> --help")} for more information.
7639
8150
  `
7640
8151
  );
7641
8152
  process.exit(0);
@@ -7697,9 +8208,9 @@ var flags = args.parse(process.argv);
7697
8208
  var [primaryCommand, subCommand] = args.sub;
7698
8209
  if (!primaryCommand) {
7699
8210
  async function selectService() {
7700
- clack13.intro(pc14.bold(`WRAPS CLI v${VERSION}`));
8211
+ clack14.intro(pc15.bold(`WRAPS CLI v${VERSION}`));
7701
8212
  console.log("Welcome! Let's get started deploying your infrastructure.\n");
7702
- const service = await clack13.select({
8213
+ const service = await clack14.select({
7703
8214
  message: "Which service would you like to set up?",
7704
8215
  options: [
7705
8216
  {
@@ -7714,20 +8225,20 @@ if (!primaryCommand) {
7714
8225
  }
7715
8226
  ]
7716
8227
  });
7717
- if (clack13.isCancel(service)) {
7718
- clack13.cancel("Operation cancelled.");
8228
+ if (clack14.isCancel(service)) {
8229
+ clack14.cancel("Operation cancelled.");
7719
8230
  process.exit(0);
7720
8231
  }
7721
8232
  if (service === "sms") {
7722
- clack13.log.warn("SMS infrastructure is coming soon!");
8233
+ clack14.log.warn("SMS infrastructure is coming soon!");
7723
8234
  console.log(
7724
8235
  `
7725
- Check back soon or follow our progress at ${pc14.cyan("https://github.com/wraps-team/wraps")}
8236
+ Check back soon or follow our progress at ${pc15.cyan("https://github.com/wraps-team/wraps")}
7726
8237
  `
7727
8238
  );
7728
8239
  process.exit(0);
7729
8240
  }
7730
- const action = await clack13.select({
8241
+ const action = await clack14.select({
7731
8242
  message: "What would you like to do?",
7732
8243
  options: [
7733
8244
  {
@@ -7742,8 +8253,8 @@ Check back soon or follow our progress at ${pc14.cyan("https://github.com/wraps-
7742
8253
  }
7743
8254
  ]
7744
8255
  });
7745
- if (clack13.isCancel(action)) {
7746
- clack13.cancel("Operation cancelled.");
8256
+ if (clack14.isCancel(action)) {
8257
+ clack14.cancel("Operation cancelled.");
7747
8258
  process.exit(0);
7748
8259
  }
7749
8260
  if (action === "init") {
@@ -7766,6 +8277,27 @@ Check back soon or follow our progress at ${pc14.cyan("https://github.com/wraps-
7766
8277
  process.exit(0);
7767
8278
  }
7768
8279
  async function run() {
8280
+ const startTime = Date.now();
8281
+ const telemetry = getTelemetryClient();
8282
+ if (telemetry.shouldShowNotification()) {
8283
+ console.log();
8284
+ clack14.log.info(pc15.bold("Anonymous Telemetry"));
8285
+ console.log(
8286
+ ` Wraps collects ${pc15.cyan("anonymous usage data")} to improve the CLI.`
8287
+ );
8288
+ console.log(
8289
+ ` We ${pc15.bold("never")} collect: domains, AWS credentials, email content, or PII.`
8290
+ );
8291
+ console.log(
8292
+ ` We ${pc15.bold("only")} collect: command names, success/failure, CLI version, OS.`
8293
+ );
8294
+ console.log();
8295
+ console.log(` Opt-out anytime: ${pc15.cyan("wraps telemetry disable")}`);
8296
+ console.log(` Or set: ${pc15.cyan("WRAPS_TELEMETRY_DISABLED=1")}`);
8297
+ console.log(` Learn more: ${pc15.cyan("https://wraps.dev/docs/telemetry")}`);
8298
+ console.log();
8299
+ telemetry.markNotificationShown();
8300
+ }
7769
8301
  try {
7770
8302
  if (primaryCommand === "email" && subCommand) {
7771
8303
  switch (subCommand) {
@@ -7808,10 +8340,10 @@ async function run() {
7808
8340
  switch (domainsSubCommand) {
7809
8341
  case "add": {
7810
8342
  if (!flags.domain) {
7811
- clack13.log.error("--domain flag is required");
8343
+ clack14.log.error("--domain flag is required");
7812
8344
  console.log(
7813
8345
  `
7814
- Usage: ${pc14.cyan("wraps email domains add --domain yourapp.com")}
8346
+ Usage: ${pc15.cyan("wraps email domains add --domain yourapp.com")}
7815
8347
  `
7816
8348
  );
7817
8349
  process.exit(1);
@@ -7824,10 +8356,10 @@ Usage: ${pc14.cyan("wraps email domains add --domain yourapp.com")}
7824
8356
  break;
7825
8357
  case "verify": {
7826
8358
  if (!flags.domain) {
7827
- clack13.log.error("--domain flag is required");
8359
+ clack14.log.error("--domain flag is required");
7828
8360
  console.log(
7829
8361
  `
7830
- Usage: ${pc14.cyan("wraps email domains verify --domain yourapp.com")}
8362
+ Usage: ${pc15.cyan("wraps email domains verify --domain yourapp.com")}
7831
8363
  `
7832
8364
  );
7833
8365
  process.exit(1);
@@ -7837,10 +8369,10 @@ Usage: ${pc14.cyan("wraps email domains verify --domain yourapp.com")}
7837
8369
  }
7838
8370
  case "get-dkim": {
7839
8371
  if (!flags.domain) {
7840
- clack13.log.error("--domain flag is required");
8372
+ clack14.log.error("--domain flag is required");
7841
8373
  console.log(
7842
8374
  `
7843
- Usage: ${pc14.cyan("wraps email domains get-dkim --domain yourapp.com")}
8375
+ Usage: ${pc15.cyan("wraps email domains get-dkim --domain yourapp.com")}
7844
8376
  `
7845
8377
  );
7846
8378
  process.exit(1);
@@ -7850,10 +8382,10 @@ Usage: ${pc14.cyan("wraps email domains get-dkim --domain yourapp.com")}
7850
8382
  }
7851
8383
  case "remove": {
7852
8384
  if (!flags.domain) {
7853
- clack13.log.error("--domain flag is required");
8385
+ clack14.log.error("--domain flag is required");
7854
8386
  console.log(
7855
8387
  `
7856
- Usage: ${pc14.cyan("wraps email domains remove --domain yourapp.com --force")}
8388
+ Usage: ${pc15.cyan("wraps email domains remove --domain yourapp.com --force")}
7857
8389
  `
7858
8390
  );
7859
8391
  process.exit(1);
@@ -7865,12 +8397,12 @@ Usage: ${pc14.cyan("wraps email domains remove --domain yourapp.com --force")}
7865
8397
  break;
7866
8398
  }
7867
8399
  default:
7868
- clack13.log.error(
8400
+ clack14.log.error(
7869
8401
  `Unknown domains command: ${domainsSubCommand || "(none)"}`
7870
8402
  );
7871
8403
  console.log(
7872
8404
  `
7873
- Available commands: ${pc14.cyan("add")}, ${pc14.cyan("list")}, ${pc14.cyan("verify")}, ${pc14.cyan("get-dkim")}, ${pc14.cyan("remove")}
8405
+ Available commands: ${pc15.cyan("add")}, ${pc15.cyan("list")}, ${pc15.cyan("verify")}, ${pc15.cyan("get-dkim")}, ${pc15.cyan("remove")}
7874
8406
  `
7875
8407
  );
7876
8408
  process.exit(1);
@@ -7878,10 +8410,10 @@ Available commands: ${pc14.cyan("add")}, ${pc14.cyan("list")}, ${pc14.cyan("veri
7878
8410
  break;
7879
8411
  }
7880
8412
  default:
7881
- clack13.log.error(`Unknown email command: ${subCommand}`);
8413
+ clack14.log.error(`Unknown email command: ${subCommand}`);
7882
8414
  console.log(
7883
8415
  `
7884
- Run ${pc14.cyan("wraps --help")} for available commands.
8416
+ Run ${pc15.cyan("wraps --help")} for available commands.
7885
8417
  `
7886
8418
  );
7887
8419
  process.exit(1);
@@ -7897,21 +8429,21 @@ Run ${pc14.cyan("wraps --help")} for available commands.
7897
8429
  });
7898
8430
  break;
7899
8431
  default:
7900
- clack13.log.error(`Unknown dashboard command: ${subCommand}`);
8432
+ clack14.log.error(`Unknown dashboard command: ${subCommand}`);
7901
8433
  console.log(`
7902
- Available commands: ${pc14.cyan("update-role")}
8434
+ Available commands: ${pc15.cyan("update-role")}
7903
8435
  `);
7904
- console.log(`Run ${pc14.cyan("wraps --help")} for more information.
8436
+ console.log(`Run ${pc15.cyan("wraps --help")} for more information.
7905
8437
  `);
7906
8438
  process.exit(1);
7907
8439
  }
7908
8440
  return;
7909
8441
  }
7910
8442
  if (primaryCommand === "sms" && subCommand) {
7911
- clack13.log.warn("SMS infrastructure is coming soon!");
8443
+ clack14.log.warn("SMS infrastructure is coming soon!");
7912
8444
  console.log(
7913
8445
  `
7914
- Check back soon or follow our progress at ${pc14.cyan("https://github.com/wraps-team/wraps")}
8446
+ Check back soon or follow our progress at ${pc15.cyan("https://github.com/wraps-team/wraps")}
7915
8447
  `
7916
8448
  );
7917
8449
  process.exit(0);
@@ -7931,8 +8463,8 @@ Check back soon or follow our progress at ${pc14.cyan("https://github.com/wraps-
7931
8463
  break;
7932
8464
  case "dashboard":
7933
8465
  if (!subCommand) {
7934
- clack13.log.warn(
7935
- `'wraps dashboard' is deprecated. Use ${pc14.cyan("wraps console")} instead.`
8466
+ clack14.log.warn(
8467
+ `'wraps dashboard' is deprecated. Use ${pc15.cyan("wraps console")} instead.`
7936
8468
  );
7937
8469
  await dashboard({
7938
8470
  port: flags.port,
@@ -7948,6 +8480,29 @@ Check back soon or follow our progress at ${pc14.cyan("https://github.com/wraps-
7948
8480
  case "completion":
7949
8481
  printCompletionScript();
7950
8482
  break;
8483
+ case "telemetry": {
8484
+ switch (subCommand) {
8485
+ case "enable":
8486
+ await telemetryEnable();
8487
+ break;
8488
+ case "disable":
8489
+ await telemetryDisable();
8490
+ break;
8491
+ case "status":
8492
+ case void 0:
8493
+ await telemetryStatus();
8494
+ break;
8495
+ default:
8496
+ clack14.log.error(`Unknown telemetry command: ${subCommand}`);
8497
+ console.log(
8498
+ `
8499
+ Available commands: ${pc15.cyan("enable")}, ${pc15.cyan("disable")}, ${pc15.cyan("status")}
8500
+ `
8501
+ );
8502
+ process.exit(1);
8503
+ }
8504
+ break;
8505
+ }
7951
8506
  // Show help for service without subcommand
7952
8507
  case "email":
7953
8508
  case "sms":
@@ -7959,16 +8514,30 @@ Please specify a command for ${primaryCommand} service.
7959
8514
  showHelp();
7960
8515
  break;
7961
8516
  default:
7962
- clack13.log.error(`Unknown command: ${primaryCommand}`);
8517
+ clack14.log.error(`Unknown command: ${primaryCommand}`);
7963
8518
  console.log(
7964
8519
  `
7965
- Run ${pc14.cyan("wraps --help")} for available commands.
8520
+ Run ${pc15.cyan("wraps --help")} for available commands.
7966
8521
  `
7967
8522
  );
7968
8523
  process.exit(1);
7969
8524
  }
8525
+ const duration = Date.now() - startTime;
8526
+ const commandName = subCommand ? `${primaryCommand}:${subCommand}` : primaryCommand;
8527
+ trackCommand(commandName, {
8528
+ success: true,
8529
+ duration_ms: duration
8530
+ });
7970
8531
  } catch (error) {
8532
+ const duration = Date.now() - startTime;
8533
+ const commandName = subCommand ? `${primaryCommand}:${subCommand}` : primaryCommand;
8534
+ trackCommand(commandName, {
8535
+ success: false,
8536
+ duration_ms: duration
8537
+ });
7971
8538
  handleCLIError(error);
8539
+ } finally {
8540
+ await telemetry.shutdown();
7972
8541
  }
7973
8542
  }
7974
8543
  run();