@dosu/cli 0.44.0 → 0.45.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.
Files changed (3) hide show
  1. package/README.md +35 -0
  2. package/bin/dosu.js +1372 -140
  3. package/package.json +3 -1
package/bin/dosu.js CHANGED
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env node
2
+
2
3
  import { createRequire } from "node:module";
3
4
  var __create = Object.create;
4
5
  var __getProtoOf = Object.getPrototypeOf;
@@ -2113,6 +2114,9 @@ function getAccessTokenClaim(accessToken, claim) {
2113
2114
  function getAccessTokenUserID(accessToken) {
2114
2115
  return getAccessTokenClaim(accessToken, "sub");
2115
2116
  }
2117
+ function getAccessTokenEmail(accessToken) {
2118
+ return getAccessTokenClaim(accessToken, "email");
2119
+ }
2116
2120
  function getAccessTokenOAuthClientID(accessToken) {
2117
2121
  return getAccessTokenClaim(accessToken, "client_id");
2118
2122
  }
@@ -2219,15 +2223,22 @@ function loadConfig() {
2219
2223
  } catch {
2220
2224
  return emptyConfig();
2221
2225
  }
2226
+ const parsed = parseConfig(raw);
2222
2227
  if (isConfigV2(raw))
2223
- return normalizeV2(raw);
2228
+ return parsed;
2224
2229
  if (isRecord2(raw) && "schema_version" in raw)
2225
- return emptyConfig();
2226
- const migrated = migrateLegacyConfig(raw);
2230
+ return parsed;
2227
2231
  try {
2228
- writeConfig(path2, migrated);
2232
+ writeConfig(path2, parsed);
2229
2233
  } catch {}
2230
- return migrated;
2234
+ return parsed;
2235
+ }
2236
+ function parseConfig(raw) {
2237
+ if (isConfigV2(raw))
2238
+ return normalizeV2(raw);
2239
+ if (isRecord2(raw) && "schema_version" in raw)
2240
+ return emptyConfig();
2241
+ return migrateLegacyConfig(raw);
2231
2242
  }
2232
2243
  function saveConfig(cfg) {
2233
2244
  writeConfig(getConfigPath(), cfg);
@@ -4762,7 +4773,7 @@ function isNpxInvocation(channel = INSTALL_CHANNEL, env = process.env) {
4762
4773
  function getVersionString() {
4763
4774
  return `v${VERSION}`;
4764
4775
  }
4765
- var VERSION = "0.44.0", INSTALL_CHANNEL = "npm";
4776
+ var VERSION = "0.45.0", INSTALL_CHANNEL = "npm";
4766
4777
 
4767
4778
  // src/debug/logger.ts
4768
4779
  import {
@@ -10386,6 +10397,34 @@ var init_insights2 = __esm(() => {
10386
10397
  init_insights();
10387
10398
  });
10388
10399
 
10400
+ // src/auth/errors.ts
10401
+ var exports_errors = {};
10402
+ __export(exports_errors, {
10403
+ OAuthCallbackError: () => OAuthCallbackError
10404
+ });
10405
+ var OAuthCallbackError;
10406
+ var init_errors = __esm(() => {
10407
+ OAuthCallbackError = class OAuthCallbackError extends Error {
10408
+ error;
10409
+ errorCode;
10410
+ errorDescription;
10411
+ constructor(message, details = {}) {
10412
+ super(message);
10413
+ this.name = "OAuthCallbackError";
10414
+ this.error = details.error;
10415
+ this.errorCode = details.errorCode;
10416
+ this.errorDescription = details.errorDescription;
10417
+ }
10418
+ get userMessage() {
10419
+ const desc = this.errorDescription ?? this.message;
10420
+ if (this.errorCode === "bad_oauth_state" || /state/i.test(desc)) {
10421
+ return `Authentication failed: ${desc}. Run \`dosu login\` again.`;
10422
+ }
10423
+ return `Authentication failed: ${desc}`;
10424
+ }
10425
+ };
10426
+ });
10427
+
10389
10428
  // src/version/skill-update-check.ts
10390
10429
  import { existsSync as existsSync12, mkdirSync as mkdirSync7, readFileSync as readFileSync12, writeFileSync as writeFileSync6 } from "node:fs";
10391
10430
  import { join as join10 } from "node:path";
@@ -11553,56 +11592,1013 @@ var init_agents_md_step = __esm(() => {
11553
11592
  SECTION_START_RE = /<!-- dosu:mcp:start(?: v(\d+))? -->/;
11554
11593
  });
11555
11594
 
11595
+ // src/telemetry/settings.ts
11596
+ import { randomUUID } from "node:crypto";
11597
+ import {
11598
+ existsSync as existsSync18,
11599
+ lstatSync,
11600
+ mkdirSync as mkdirSync9,
11601
+ readFileSync as readFileSync16,
11602
+ renameSync as renameSync3,
11603
+ unlinkSync as unlinkSync2,
11604
+ writeFileSync as writeFileSync9
11605
+ } from "node:fs";
11606
+ import { join as join29 } from "node:path";
11607
+ function getTelemetrySettingsPath() {
11608
+ return join29(getConfigDir(), TELEMETRY_SETTINGS_FILENAME);
11609
+ }
11610
+ function loadTelemetrySettings() {
11611
+ return readTelemetrySettings().settings;
11612
+ }
11613
+ function setTelemetryEnabled(enabled) {
11614
+ if (typeof enabled !== "boolean")
11615
+ return false;
11616
+ const current = readTelemetrySettings();
11617
+ if (current.status === "unsupported" || current.status === "error")
11618
+ return false;
11619
+ const settings2 = current.status === "current" ? current.settings : emptyTelemetrySettings();
11620
+ if (enabled)
11621
+ delete settings2.disabled;
11622
+ else
11623
+ settings2.disabled = true;
11624
+ return writeTelemetrySettings(settings2);
11625
+ }
11626
+ function telemetryDisabledByEnvironment() {
11627
+ if (isEnabledEnvironmentFlag(process.env.DO_NOT_TRACK))
11628
+ return "DO_NOT_TRACK";
11629
+ if (isEnabledEnvironmentFlag(process.env.DOSU_TELEMETRY_DISABLED)) {
11630
+ return "DOSU_TELEMETRY_DISABLED";
11631
+ }
11632
+ return;
11633
+ }
11634
+ function isTelemetryEnabled(settings2 = loadTelemetrySettings()) {
11635
+ if (telemetryDisabledByEnvironment() !== undefined)
11636
+ return false;
11637
+ return settings2.disabled !== true;
11638
+ }
11639
+ function getOrCreateInstallID() {
11640
+ const current = readTelemetrySettings();
11641
+ if (current.status === "current" && current.settings.install_id) {
11642
+ return current.settings.install_id;
11643
+ }
11644
+ if (current.status !== "current" && current.status !== "missing")
11645
+ return;
11646
+ const installID = randomUUID();
11647
+ const settings2 = current.status === "current" ? current.settings : emptyTelemetrySettings();
11648
+ settings2.install_id = installID;
11649
+ return writeTelemetrySettings(settings2) ? installID : undefined;
11650
+ }
11651
+ function resetInstallID() {
11652
+ const current = readTelemetrySettings();
11653
+ if (current.status !== "current" && current.status !== "missing")
11654
+ return;
11655
+ const settings2 = current.status === "current" ? current.settings : emptyTelemetrySettings();
11656
+ const installID = randomUUID();
11657
+ settings2.install_id = installID;
11658
+ return writeTelemetrySettings(settings2) ? installID : undefined;
11659
+ }
11660
+ function readTelemetrySettings() {
11661
+ const path3 = getTelemetrySettingsPath();
11662
+ let file;
11663
+ try {
11664
+ file = lstatSync(path3);
11665
+ } catch (error) {
11666
+ if (isErrorCode(error, "ENOENT")) {
11667
+ return { status: "missing", settings: emptyTelemetrySettings() };
11668
+ }
11669
+ return { status: "error", settings: disabledTelemetrySettings() };
11670
+ }
11671
+ if (!file.isFile() || file.size > MAX_TELEMETRY_SETTINGS_BYTES) {
11672
+ return { status: "invalid", settings: disabledTelemetrySettings() };
11673
+ }
11674
+ let content;
11675
+ try {
11676
+ content = readFileSync16(path3, "utf-8");
11677
+ } catch {
11678
+ return { status: "error", settings: disabledTelemetrySettings() };
11679
+ }
11680
+ let raw;
11681
+ try {
11682
+ raw = JSON.parse(content);
11683
+ } catch {
11684
+ return { status: "invalid", settings: disabledTelemetrySettings() };
11685
+ }
11686
+ if (!isRecord3(raw))
11687
+ return { status: "invalid", settings: disabledTelemetrySettings() };
11688
+ if (raw.schema_version !== TELEMETRY_SCHEMA_VERSION) {
11689
+ const status = "schema_version" in raw ? "unsupported" : "invalid";
11690
+ return { status, settings: disabledTelemetrySettings() };
11691
+ }
11692
+ if ("disabled" in raw && typeof raw.disabled !== "boolean") {
11693
+ return { status: "invalid", settings: disabledTelemetrySettings() };
11694
+ }
11695
+ const settings2 = emptyTelemetrySettings();
11696
+ if (raw.disabled === true)
11697
+ settings2.disabled = true;
11698
+ if (typeof raw.install_id === "string" && isUUID(raw.install_id)) {
11699
+ settings2.install_id = raw.install_id;
11700
+ }
11701
+ return { status: "current", settings: settings2 };
11702
+ }
11703
+ function writeTelemetrySettings(settings2) {
11704
+ const path3 = getTelemetrySettingsPath();
11705
+ const dir = getConfigDir();
11706
+ const temporaryPath = `${path3}.${process.pid}.${randomUUID()}.tmp`;
11707
+ try {
11708
+ if (!existsSync18(dir))
11709
+ mkdirSync9(dir, { recursive: true, mode: 448 });
11710
+ writeFileSync9(temporaryPath, `${JSON.stringify(settings2, null, 2)}
11711
+ `, {
11712
+ flag: "wx",
11713
+ mode: 384
11714
+ });
11715
+ renameSync3(temporaryPath, path3);
11716
+ return true;
11717
+ } catch {
11718
+ try {
11719
+ unlinkSync2(temporaryPath);
11720
+ } catch {}
11721
+ return false;
11722
+ }
11723
+ }
11724
+ function emptyTelemetrySettings() {
11725
+ return { schema_version: TELEMETRY_SCHEMA_VERSION };
11726
+ }
11727
+ function disabledTelemetrySettings() {
11728
+ return { schema_version: TELEMETRY_SCHEMA_VERSION, disabled: true };
11729
+ }
11730
+ function isEnabledEnvironmentFlag(value) {
11731
+ if (value === undefined)
11732
+ return false;
11733
+ const normalized = value.trim().toLowerCase();
11734
+ if (normalized === "")
11735
+ return false;
11736
+ return !["0", "false", "no", "off"].includes(normalized);
11737
+ }
11738
+ function isUUID(value) {
11739
+ return /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value);
11740
+ }
11741
+ function isRecord3(value) {
11742
+ return typeof value === "object" && value !== null && !Array.isArray(value);
11743
+ }
11744
+ function isErrorCode(error, code) {
11745
+ return typeof error === "object" && error !== null && "code" in error && error.code === code;
11746
+ }
11747
+ var TELEMETRY_SCHEMA_VERSION = 1, TELEMETRY_SETTINGS_FILENAME = "telemetry.json", MAX_TELEMETRY_SETTINGS_BYTES;
11748
+ var init_settings = __esm(() => {
11749
+ init_config();
11750
+ MAX_TELEMETRY_SETTINGS_BYTES = 16 * 1024;
11751
+ });
11752
+
11753
+ // src/telemetry/debug-id.ts
11754
+ var BUNDLE_DEBUG_ID_PLACEHOLDER = "df4adcb4-addf-31e4-6475-6e2164756e21";
11755
+
11756
+ // src/telemetry/telemetry.ts
11757
+ import { randomUUID as randomUUID2 } from "node:crypto";
11758
+ import { existsSync as existsSync19 } from "node:fs";
11759
+ import { request as httpRequest2 } from "node:http";
11760
+ import { request as httpsRequest } from "node:https";
11761
+ import { isAbsolute, relative, resolve, sep } from "node:path";
11762
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
11763
+ function safeString(value, pattern, fallback) {
11764
+ if (typeof value !== "string" || !pattern.test(value))
11765
+ return fallback;
11766
+ return value;
11767
+ }
11768
+ function canonicalCommand(value) {
11769
+ if (typeof value !== "string")
11770
+ return UNKNOWN_COMMAND;
11771
+ const command = value.trim();
11772
+ if (command.length > 80)
11773
+ return UNKNOWN_COMMAND;
11774
+ if (!/^[a-z][a-z0-9-]*(?: [a-z][a-z0-9-]*){0,3}$/.test(command)) {
11775
+ return UNKNOWN_COMMAND;
11776
+ }
11777
+ return command;
11778
+ }
11779
+ function normalizeContext(context) {
11780
+ const isAuthenticated2 = context?.isAuthenticated === true;
11781
+ const id = safeRead(context?.user, "id");
11782
+ const email = safeRead(context?.user, "email");
11783
+ const user = isAuthenticated2 && validUserId(id) ? { id, ...validEmail(email) ? { email } : {} } : undefined;
11784
+ return {
11785
+ mode: context?.mode === "oss" ? "oss" : "cloud",
11786
+ isAuthenticated: isAuthenticated2,
11787
+ ...user ? { user } : {}
11788
+ };
11789
+ }
11790
+ function normalizeExitCode(value, fallback = 1) {
11791
+ if (typeof value !== "number" || !Number.isFinite(value))
11792
+ return fallback;
11793
+ return Math.min(255, Math.max(0, Math.trunc(value)));
11794
+ }
11795
+ function normalizeRuntime(runtime) {
11796
+ return {
11797
+ version: safeString(runtime.version, /^[A-Za-z0-9.+_-]{1,32}$/, "unknown"),
11798
+ installChannel: safeString(runtime.installChannel, /^[A-Za-z][A-Za-z0-9_-]{0,31}$/, "unknown"),
11799
+ platform: safeString(runtime.platform, /^[A-Za-z0-9_-]{1,24}$/, "unknown"),
11800
+ arch: safeString(runtime.arch, /^[A-Za-z0-9_-]{1,24}$/, "unknown"),
11801
+ runtime: safeString(runtime.runtime, /^[A-Za-z][A-Za-z0-9_-]{0,23}$/, "unknown"),
11802
+ runtimeMajor: Math.min(9999, Math.max(0, Math.trunc(runtime.runtimeMajor) || 0)),
11803
+ isCi: runtime.isCi === true,
11804
+ isTty: runtime.isTty === true
11805
+ };
11806
+ }
11807
+ function validInstallId(value) {
11808
+ return typeof value === "string" && /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value);
11809
+ }
11810
+ function validUserId(value) {
11811
+ return validInstallId(value);
11812
+ }
11813
+ function validEmail(value) {
11814
+ return typeof value === "string" && value.length <= 254 && /^[A-Za-z0-9.!#$%&'*+/=?^_`{|}~-]+@[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?(?:\.[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?)+$/.test(value);
11815
+ }
11816
+ function validEventId(value) {
11817
+ return typeof value === "string" && /^[0-9a-f]{32}$/i.test(value);
11818
+ }
11819
+ function validDebugId(value) {
11820
+ return typeof value === "string" && /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value);
11821
+ }
11822
+ function validErrorType(value) {
11823
+ return typeof value === "string" && SAFE_ERROR_TYPES.has(value);
11824
+ }
11825
+ function validErrorCode(value) {
11826
+ return typeof value === "string" && SAFE_ERROR_CODES.has(value);
11827
+ }
11828
+ function validStatus(value) {
11829
+ return typeof value === "number" && Number.isInteger(value) && value >= 100 && value <= 599;
11830
+ }
11831
+ function objectLike(value) {
11832
+ return typeof value === "object" && value !== null || typeof value === "function";
11833
+ }
11834
+ function safeRead(value, key) {
11835
+ if (!objectLike(value))
11836
+ return;
11837
+ try {
11838
+ return Reflect.get(value, key);
11839
+ } catch {
11840
+ return;
11841
+ }
11842
+ }
11843
+ function parseStack(stack) {
11844
+ if (typeof stack !== "string" || !CLI_PACKAGE_ROOT)
11845
+ return [];
11846
+ const frames = [];
11847
+ const lines = stack.slice(0, 1e5).split(`
11848
+ `, 200);
11849
+ for (const originalLine of lines) {
11850
+ if (frames.length >= MAX_STACK_FRAMES)
11851
+ break;
11852
+ const parsed = parseOwnedStackLine(originalLine, CLI_PACKAGE_ROOT);
11853
+ if (!parsed)
11854
+ continue;
11855
+ const { filename, lineno, colno } = parsed;
11856
+ if (!Number.isSafeInteger(lineno) || !Number.isSafeInteger(colno) || lineno < 1 || colno < 1 || lineno > 1e7 || colno > 1e7) {
11857
+ continue;
11858
+ }
11859
+ frames.push({ filename, lineno, colno, in_app: true });
11860
+ }
11861
+ return frames;
11862
+ }
11863
+ function detectCliPackageRoot() {
11864
+ try {
11865
+ const modulePath = fileURLToPath2(import.meta.url).replaceAll("\\", "/");
11866
+ for (const suffix of ["/src/telemetry/telemetry.ts", "/bin/dosu.js"]) {
11867
+ if (modulePath.endsWith(suffix))
11868
+ return modulePath.slice(0, -suffix.length);
11869
+ }
11870
+ } catch {}
11871
+ return;
11872
+ }
11873
+ function parseOwnedStackLine(originalLine, packageRoot) {
11874
+ const line = originalLine.replaceAll("\\", "/");
11875
+ const match = line.match(/^\s*at\s+.*\s+\((.+):(\d+):(\d+)\)\s*$/) ?? line.match(/^\s*at\s+(.+):(\d+):(\d+)\s*$/);
11876
+ if (!match?.[1] || !match[2] || !match[3])
11877
+ return;
11878
+ let absolutePath = match[1];
11879
+ try {
11880
+ if (absolutePath.startsWith("file://"))
11881
+ absolutePath = fileURLToPath2(absolutePath);
11882
+ } catch {
11883
+ return;
11884
+ }
11885
+ if (!isAbsolute(absolutePath))
11886
+ return;
11887
+ const resolvedPath = resolve(absolutePath);
11888
+ const relativePath = relative(packageRoot, resolvedPath);
11889
+ if (relativePath === "" || relativePath === ".." || relativePath.startsWith(`..${sep}`) || isAbsolute(relativePath)) {
11890
+ return;
11891
+ }
11892
+ const filename = relativePath.replaceAll("\\", "/");
11893
+ if (!/^(?:src\/(?:[A-Za-z0-9_.-]+\/)*[A-Za-z0-9_.-]+\.(?:[cm]?[jt]sx?)|bin\/dosu\.js)$/.test(filename)) {
11894
+ return;
11895
+ }
11896
+ try {
11897
+ if (!existsSync19(resolvedPath))
11898
+ return;
11899
+ } catch {
11900
+ return;
11901
+ }
11902
+ return { filename, lineno: Number(match[2]), colno: Number(match[3]) };
11903
+ }
11904
+ function sanitizeError(error) {
11905
+ const data = safeRead(error, "data");
11906
+ const typeCandidate = safeRead(error, "name");
11907
+ const codeCandidates = [safeRead(data, "code"), safeRead(error, "code")];
11908
+ const statusCandidates = [
11909
+ safeRead(data, "httpStatus"),
11910
+ safeRead(error, "status"),
11911
+ safeRead(error, "statusCode")
11912
+ ];
11913
+ const exitCodeCandidate = safeRead(error, "exitCode");
11914
+ const code = codeCandidates.find(validErrorCode);
11915
+ const status = statusCandidates.find(validStatus);
11916
+ const exitCode = typeof exitCodeCandidate === "number" && Number.isFinite(exitCodeCandidate) ? normalizeExitCode(exitCodeCandidate) : undefined;
11917
+ return {
11918
+ type: validErrorType(typeCandidate) ? typeCandidate : "Error",
11919
+ ...code ? { code } : {},
11920
+ ...status ? { status } : {},
11921
+ frames: parseStack(safeRead(error, "stack")),
11922
+ ...exitCode === undefined ? {} : { exitCode }
11923
+ };
11924
+ }
11925
+ function durationBucket(durationMs) {
11926
+ const duration = Number.isFinite(durationMs) ? Math.max(0, durationMs) : 0;
11927
+ if (duration < 100)
11928
+ return "<100ms";
11929
+ if (duration < 500)
11930
+ return "100-499ms";
11931
+ if (duration < 2000)
11932
+ return "500ms-1.9s";
11933
+ if (duration < 1e4)
11934
+ return "2-9.9s";
11935
+ if (duration < 60000)
11936
+ return "10-59s";
11937
+ return "60s+";
11938
+ }
11939
+ function buildPostHogPayload(input) {
11940
+ const context = normalizeContext(input.context);
11941
+ const runtime = normalizeRuntime(input.runtime);
11942
+ const errorCode = validErrorCode(input.errorCode) ? input.errorCode : undefined;
11943
+ const result = input.result === "success" || input.result === "validation_error" ? input.result : "failure";
11944
+ return {
11945
+ api_key: input.apiKey,
11946
+ distinct_id: context.user?.id ?? (validInstallId(input.installId) ? input.installId : FALLBACK_INSTALL_ID),
11947
+ event: "cli_command_completed",
11948
+ properties: {
11949
+ $geoip_disable: true,
11950
+ ...context.user ? {} : { $process_person_profile: false },
11951
+ schema_version: 1,
11952
+ command: canonicalCommand(input.command),
11953
+ result,
11954
+ duration_bucket: durationBucket(input.durationMs),
11955
+ cli_version: runtime.version,
11956
+ install_channel: runtime.installChannel,
11957
+ platform: runtime.platform,
11958
+ arch: runtime.arch,
11959
+ runtime: runtime.runtime,
11960
+ runtime_major: runtime.runtimeMajor,
11961
+ is_ci: runtime.isCi,
11962
+ is_tty: runtime.isTty,
11963
+ mode: context.mode,
11964
+ is_authenticated: context.isAuthenticated,
11965
+ exit_code: normalizeExitCode(input.exitCode),
11966
+ ...errorCode ? { error_code: errorCode } : {}
11967
+ }
11968
+ };
11969
+ }
11970
+ function parsePostHogProjectToken(value) {
11971
+ if (typeof value !== "string")
11972
+ return null;
11973
+ const token = value.trim();
11974
+ return /^phc_[A-Za-z0-9_-]{3,252}$/.test(token) ? token : null;
11975
+ }
11976
+ function parseTelemetryWebAppURL(value, allowInsecureLoopback = false) {
11977
+ if (typeof value !== "string" || value.length === 0 || value.length > 2048)
11978
+ return null;
11979
+ try {
11980
+ const url = new URL(value.trim());
11981
+ if (!url.hostname || url.username || url.password || url.search || url.hash)
11982
+ return null;
11983
+ const loopbackHosts = new Set(["localhost", "127.0.0.1", "[::1]"]);
11984
+ const secure = url.protocol === "https:";
11985
+ const allowedDevelopmentOrigin = allowInsecureLoopback && url.protocol === "http:" && loopbackHosts.has(url.hostname);
11986
+ if (!secure && !allowedDevelopmentOrigin)
11987
+ return null;
11988
+ return url.toString().replace(/\/$/, "");
11989
+ } catch {
11990
+ return null;
11991
+ }
11992
+ }
11993
+ function parseSentryDsn(value) {
11994
+ if (typeof value !== "string" || value.length === 0 || value.length > 2048)
11995
+ return null;
11996
+ const dsn = value.trim();
11997
+ try {
11998
+ const url = new URL(dsn);
11999
+ if (url.protocol !== "https:" || !url.hostname || !url.username || url.password || url.search || url.hash || !/^[A-Za-z0-9_.-]{1,128}$/.test(url.username) || url.username.toLowerCase().startsWith("sntry")) {
12000
+ return null;
12001
+ }
12002
+ const segments = url.pathname.split("/").filter(Boolean);
12003
+ const projectId = segments.pop();
12004
+ if (!projectId || !/^[A-Za-z0-9_-]{1,128}$/.test(projectId))
12005
+ return null;
12006
+ if (segments.some((segment) => !/^[A-Za-z0-9._~-]{1,128}$/.test(segment)))
12007
+ return null;
12008
+ const prefix = segments.length > 0 ? `/${segments.join("/")}` : "";
12009
+ return {
12010
+ dsn,
12011
+ endpoint: `${url.protocol}//${url.host}${prefix}/api/${projectId}/envelope/`,
12012
+ projectId,
12013
+ publicKey: url.username
12014
+ };
12015
+ } catch {
12016
+ return null;
12017
+ }
12018
+ }
12019
+ function sentryTags(command, context, runtime, error) {
12020
+ return {
12021
+ schema_version: "1",
12022
+ command,
12023
+ cli_version: runtime.version,
12024
+ install_channel: runtime.installChannel,
12025
+ os: runtime.platform,
12026
+ arch: runtime.arch,
12027
+ runtime: runtime.runtime,
12028
+ runtime_major: String(runtime.runtimeMajor),
12029
+ is_ci: String(runtime.isCi),
12030
+ is_tty: String(runtime.isTty),
12031
+ mode: context.mode,
12032
+ is_authenticated: String(context.isAuthenticated),
12033
+ ...error.code ? { error_code: error.code } : {},
12034
+ ...error.status ? { http_status: String(error.status) } : {},
12035
+ ...error.exitCode === undefined ? {} : { exit_code: String(error.exitCode) }
12036
+ };
12037
+ }
12038
+ function buildSentryEnvelope(input) {
12039
+ const parsedDsn = parseSentryDsn(input.dsn);
12040
+ if (!parsedDsn || !validEventId(input.eventId))
12041
+ return null;
12042
+ const context = normalizeContext(input.context);
12043
+ const runtime = normalizeRuntime(input.runtime);
12044
+ const command = canonicalCommand(input.command);
12045
+ const debugId = validDebugId(input.debugId) ? input.debugId.toLowerCase() : undefined;
12046
+ const error = {
12047
+ type: validErrorType(input.error.type) ? input.error.type : "Error",
12048
+ ...validErrorCode(input.error.code) ? { code: input.error.code } : {},
12049
+ ...validStatus(input.error.status) ? { status: input.error.status } : {},
12050
+ ...typeof input.error.exitCode === "number" && Number.isFinite(input.error.exitCode) ? { exitCode: normalizeExitCode(input.error.exitCode) } : {},
12051
+ frames: input.error.frames.slice(0, MAX_STACK_FRAMES).map((frame) => {
12052
+ const filename = /^(?:src\/(?:[A-Za-z0-9_.-]+\/)*[A-Za-z0-9_.-]+\.(?:[cm]?[jt]sx?)|bin\/dosu\.js)$/.test(frame.filename) ? frame.filename : "src/unknown.ts";
12053
+ return {
12054
+ filename,
12055
+ ...debugId && filename === "bin/dosu.js" ? { abs_path: BUNDLE_CODE_FILE } : {},
12056
+ lineno: Math.min(1e7, Math.max(1, Math.trunc(frame.lineno) || 1)),
12057
+ colno: Math.min(1e7, Math.max(1, Math.trunc(frame.colno) || 1)),
12058
+ in_app: true
12059
+ };
12060
+ }).reverse()
12061
+ };
12062
+ const exceptionValue = {
12063
+ type: error.type,
12064
+ value: error.code ?? error.type,
12065
+ ...error.frames.length > 0 ? { stacktrace: { frames: error.frames } } : {}
12066
+ };
12067
+ const newestFrame = error.frames.at(-1);
12068
+ const safeCallsite = newestFrame ? `${newestFrame.filename}:${newestFrame.lineno}` : "unknown";
12069
+ const timestampMs = Number.isFinite(input.timestampMs) ? Math.max(0, input.timestampMs) : 0;
12070
+ const mappedBundle = debugId && error.frames.some((frame) => frame.abs_path === BUNDLE_CODE_FILE);
12071
+ const event = {
12072
+ event_id: input.eventId.toLowerCase(),
12073
+ timestamp: timestampMs / 1000,
12074
+ platform: "node",
12075
+ level: "error",
12076
+ release: `dosu-cli@${runtime.version}`,
12077
+ tags: sentryTags(command, context, runtime, error),
12078
+ ...context.user ? { user: context.user } : {},
12079
+ ...mappedBundle ? {
12080
+ debug_meta: {
12081
+ images: [{ type: "sourcemap", code_file: BUNDLE_CODE_FILE, debug_id: debugId }]
12082
+ }
12083
+ } : {},
12084
+ fingerprint: ["dosu-cli", command, error.type, error.code ?? "unknown", safeCallsite],
12085
+ exception: { values: [exceptionValue] }
12086
+ };
12087
+ const envelopeHeader = {
12088
+ event_id: input.eventId.toLowerCase(),
12089
+ dsn: parsedDsn.dsn,
12090
+ sent_at: new Date(timestampMs).toISOString()
12091
+ };
12092
+ return {
12093
+ endpoint: parsedDsn.endpoint,
12094
+ body: `${JSON.stringify(envelopeHeader)}
12095
+ ${JSON.stringify({ type: "event" })}
12096
+ ${JSON.stringify(event)}`
12097
+ };
12098
+ }
12099
+ function fetchWithoutRedirect(url, init = {}) {
12100
+ let parsedURL;
12101
+ try {
12102
+ parsedURL = new URL(url);
12103
+ if (parsedURL.protocol !== "http:" && parsedURL.protocol !== "https:") {
12104
+ return Promise.reject(new Error("unsupported telemetry protocol"));
12105
+ }
12106
+ } catch (error) {
12107
+ return Promise.reject(error);
12108
+ }
12109
+ return new Promise((resolve2, reject) => {
12110
+ let request;
12111
+ let response;
12112
+ let settled = false;
12113
+ const signal = init.signal;
12114
+ const cleanup = () => signal?.removeEventListener("abort", onAbort);
12115
+ const fail = (error) => {
12116
+ if (settled)
12117
+ return;
12118
+ settled = true;
12119
+ cleanup();
12120
+ response?.destroy();
12121
+ request?.socket?.destroy();
12122
+ request?.destroy();
12123
+ reject(error);
12124
+ };
12125
+ const succeed = (value) => {
12126
+ if (settled)
12127
+ return;
12128
+ settled = true;
12129
+ cleanup();
12130
+ response?.destroy();
12131
+ request?.socket?.destroy();
12132
+ request?.destroy();
12133
+ resolve2(value);
12134
+ };
12135
+ const onAbort = () => {
12136
+ const error = new Error("telemetry request aborted");
12137
+ error.name = "AbortError";
12138
+ fail(error);
12139
+ };
12140
+ try {
12141
+ if (signal?.aborted) {
12142
+ onAbort();
12143
+ return;
12144
+ }
12145
+ const body = init.body;
12146
+ if (body !== undefined && body !== null && typeof body !== "string") {
12147
+ fail(new Error("unsupported telemetry request body"));
12148
+ return;
12149
+ }
12150
+ const headers = Object.fromEntries(new Headers(init.headers).entries());
12151
+ const transport = parsedURL.protocol === "https:" ? httpsRequest : httpRequest2;
12152
+ request = transport(parsedURL, {
12153
+ method: init.method ?? "GET",
12154
+ headers,
12155
+ agent: false
12156
+ }, (incoming) => {
12157
+ response = incoming;
12158
+ const chunks = [];
12159
+ let totalBytes = 0;
12160
+ incoming.on("data", (chunk) => {
12161
+ const bytes = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
12162
+ totalBytes += bytes.byteLength;
12163
+ if (totalBytes > MAX_RESPONSE_BYTES) {
12164
+ fail(new Error("telemetry response too large"));
12165
+ return;
12166
+ }
12167
+ chunks.push(bytes);
12168
+ });
12169
+ incoming.once("error", fail);
12170
+ incoming.once("end", () => {
12171
+ try {
12172
+ const responseHeaders = new Headers;
12173
+ for (let index = 0;index < incoming.rawHeaders.length; index += 2) {
12174
+ const name = incoming.rawHeaders[index];
12175
+ const value = incoming.rawHeaders[index + 1];
12176
+ if (name && value)
12177
+ responseHeaders.append(name, value);
12178
+ }
12179
+ const status = incoming.statusCode;
12180
+ if (!status)
12181
+ throw new Error("telemetry response missing status");
12182
+ succeed(new Response(chunks.length > 0 ? Buffer.concat(chunks) : null, {
12183
+ status,
12184
+ statusText: incoming.statusMessage,
12185
+ headers: responseHeaders
12186
+ }));
12187
+ } catch (error) {
12188
+ fail(error);
12189
+ }
12190
+ });
12191
+ });
12192
+ request.once("error", fail);
12193
+ signal?.addEventListener("abort", onAbort, { once: true });
12194
+ request.end(body);
12195
+ } catch (error) {
12196
+ fail(error);
12197
+ }
12198
+ });
12199
+ }
12200
+ async function sendHttpsRequest(url, init, fetcher = fetchWithoutRedirect) {
12201
+ try {
12202
+ if (new URL(url).protocol !== "https:")
12203
+ return false;
12204
+ } catch {
12205
+ return false;
12206
+ }
12207
+ const controller = new AbortController;
12208
+ let timer;
12209
+ const timeout = new Promise((_resolve, reject) => {
12210
+ timer = setTimeout(() => {
12211
+ controller.abort();
12212
+ reject(new Error("telemetry request timed out"));
12213
+ }, REQUEST_TIMEOUT_MS);
12214
+ });
12215
+ try {
12216
+ const request = fetcher(url, { ...init, redirect: "error", signal: controller.signal });
12217
+ const result = await Promise.race([request, timeout]);
12218
+ return result.ok;
12219
+ } catch {
12220
+ return false;
12221
+ } finally {
12222
+ controller.abort();
12223
+ if (timer !== undefined)
12224
+ clearTimeout(timer);
12225
+ }
12226
+ }
12227
+ function environmentFlag(value) {
12228
+ if (value === undefined)
12229
+ return false;
12230
+ const normalized = value.trim().toLowerCase();
12231
+ return normalized !== "" && !["0", "false", "no", "off"].includes(normalized);
12232
+ }
12233
+ function runtimeNameAndMajor() {
12234
+ const versions = process.versions;
12235
+ const runtime = versions.bun ? "bun" : "node";
12236
+ const version = versions.bun ?? versions.node;
12237
+ return { runtime, runtimeMajor: Number.parseInt(version.split(".")[0] ?? "0", 10) || 0 };
12238
+ }
12239
+ function resolveRuntime(dependencies, env) {
12240
+ const detected = runtimeNameAndMajor();
12241
+ return normalizeRuntime({
12242
+ version: dependencies.version ?? VERSION,
12243
+ installChannel: dependencies.installChannel ?? INSTALL_CHANNEL,
12244
+ platform: dependencies.platform ?? process.platform,
12245
+ arch: dependencies.arch ?? process.arch,
12246
+ runtime: dependencies.runtime ?? detected.runtime,
12247
+ runtimeMajor: dependencies.runtimeMajor ?? detected.runtimeMajor,
12248
+ isCi: dependencies.isCi ?? environmentFlag(env.CI),
12249
+ isTty: dependencies.isTty ?? process.stdout.isTTY === true
12250
+ });
12251
+ }
12252
+ function validationFailure(exitCode, error) {
12253
+ if (exitCode === 2)
12254
+ return true;
12255
+ if (error?.type === "CliUsageError")
12256
+ return true;
12257
+ if (!error?.code)
12258
+ return false;
12259
+ return error.code.startsWith("commander.") || ["BAD_REQUEST", "INVALID_ARGUMENT", "UNPROCESSABLE_CONTENT", "VALIDATION_ERROR"].includes(error.code);
12260
+ }
12261
+ function safeNow(now) {
12262
+ try {
12263
+ const value = now();
12264
+ return Number.isFinite(value) ? Math.max(0, value) : 0;
12265
+ } catch {
12266
+ return 0;
12267
+ }
12268
+ }
12269
+ function safeUuid(generate) {
12270
+ try {
12271
+ const value = generate();
12272
+ return validInstallId(value) ? value : undefined;
12273
+ } catch {
12274
+ return;
12275
+ }
12276
+ }
12277
+ function eventId(generate) {
12278
+ const uuid = safeUuid(generate);
12279
+ return uuid?.replaceAll("-", "");
12280
+ }
12281
+ function createCommandTelemetry(settings2, dependencies = {}) {
12282
+ const env = dependencies.env ?? process.env;
12283
+ const fetcher = dependencies.fetch;
12284
+ const now = dependencies.now ?? Date.now;
12285
+ const generateUuid = dependencies.randomUUID ?? randomUUID2;
12286
+ const writeStderr = dependencies.stderr ?? ((payload) => process.stderr.write(`${payload}
12287
+ `));
12288
+ const webAppURL = dependencies.webAppURL ?? getWebAppURL;
12289
+ const runtime = resolveRuntime(dependencies, env);
12290
+ const debug = env.DOSU_TELEMETRY_DEBUG === "1";
12291
+ const disabled = settings2.disabled === true || environmentFlag(env.DO_NOT_TRACK) || environmentFlag(env.DOSU_TELEMETRY_DISABLED);
12292
+ const rawAnalyticsToken = dependencies.env ? env.DOSU_POSTHOG_PROJECT_TOKEN_OVERRIDE ?? env.DOSU_POSTHOG_PROJECT_TOKEN : process.env.DOSU_POSTHOG_PROJECT_TOKEN_OVERRIDE ?? "phc_Uo9RxhN0HRk8gZkPlT18BCr7eqsM0MPWVW88HZB95V5";
12293
+ const rawSentryDsn = dependencies.env ? env.DOSU_CLI_SENTRY_DSN_OVERRIDE ?? env.DOSU_CLI_SENTRY_DSN : process.env.DOSU_CLI_SENTRY_DSN_OVERRIDE ?? "https://f6b3e51dea6f50746316aa1896df76e0@o4505364738670592.ingest.us.sentry.io/4511883653283840";
12294
+ const analyticsToken = parsePostHogProjectToken(rawAnalyticsToken) ?? undefined;
12295
+ const sentryDsn = parseSentryDsn(rawSentryDsn)?.dsn;
12296
+ let started = false;
12297
+ let terminal = false;
12298
+ let startedAt = 0;
12299
+ let command = UNKNOWN_COMMAND;
12300
+ let context = normalizeContext(undefined);
12301
+ let installIdResolved = false;
12302
+ let resolvedInstallId = settings2.install_id;
12303
+ function installId() {
12304
+ if (validInstallId(resolvedInstallId))
12305
+ return resolvedInstallId;
12306
+ if (installIdResolved)
12307
+ return;
12308
+ installIdResolved = true;
12309
+ try {
12310
+ resolvedInstallId = dependencies.resolveInstallId?.();
12311
+ } catch {
12312
+ resolvedInstallId = undefined;
12313
+ }
12314
+ return validInstallId(resolvedInstallId) ? resolvedInstallId : undefined;
12315
+ }
12316
+ function debugPayload(payload) {
12317
+ try {
12318
+ writeStderr(payload);
12319
+ } catch {}
12320
+ }
12321
+ async function dispatch(result, exitCode, error) {
12322
+ try {
12323
+ const tasks = [];
12324
+ if (!disabled && analyticsToken) {
12325
+ const id = context.user?.id ?? installId();
12326
+ if (id) {
12327
+ const payload = buildPostHogPayload({
12328
+ apiKey: analyticsToken,
12329
+ ...context.user ? {} : { installId: id },
12330
+ command,
12331
+ result,
12332
+ durationMs: safeNow(now) - startedAt,
12333
+ exitCode,
12334
+ errorCode: error?.code,
12335
+ context,
12336
+ runtime
12337
+ });
12338
+ const body = JSON.stringify(payload);
12339
+ if (debug) {
12340
+ debugPayload(body);
12341
+ } else {
12342
+ const base = parseTelemetryWebAppURL(webAppURL());
12343
+ if (base) {
12344
+ tasks.push(sendHttpsRequest(`${base}/ph-api/i/v0/e/`, {
12345
+ method: "POST",
12346
+ headers: { "content-type": "application/json" },
12347
+ body
12348
+ }, fetcher));
12349
+ }
12350
+ }
12351
+ }
12352
+ }
12353
+ if (!disabled && result === "failure" && error && sentryDsn) {
12354
+ const id = eventId(generateUuid);
12355
+ const envelope = id ? buildSentryEnvelope({
12356
+ dsn: sentryDsn,
12357
+ command,
12358
+ context,
12359
+ runtime,
12360
+ error,
12361
+ eventId: id,
12362
+ timestampMs: safeNow(now),
12363
+ debugId: BUNDLE_DEBUG_ID_PLACEHOLDER
12364
+ }) : null;
12365
+ if (envelope) {
12366
+ if (debug) {
12367
+ debugPayload(envelope.body);
12368
+ } else {
12369
+ tasks.push(sendHttpsRequest(envelope.endpoint, {
12370
+ method: "POST",
12371
+ headers: { "content-type": "application/x-sentry-envelope" },
12372
+ body: envelope.body
12373
+ }, fetcher));
12374
+ }
12375
+ }
12376
+ }
12377
+ await Promise.all(tasks);
12378
+ } catch {}
12379
+ }
12380
+ return {
12381
+ start(commandName, commandContext) {
12382
+ if (started || terminal)
12383
+ return;
12384
+ started = true;
12385
+ command = canonicalCommand(commandName);
12386
+ context = normalizeContext(commandContext);
12387
+ startedAt = safeNow(now);
12388
+ },
12389
+ async complete(exitCode = 0) {
12390
+ if (!started || terminal)
12391
+ return;
12392
+ terminal = true;
12393
+ const normalizedExitCode = normalizeExitCode(exitCode, 0);
12394
+ const result = normalizedExitCode === 0 ? "success" : validationFailure(normalizedExitCode, undefined) ? "validation_error" : "failure";
12395
+ const error = result === "failure" ? { type: "CommandExitError", frames: [], exitCode: normalizedExitCode } : undefined;
12396
+ await dispatch(result, normalizedExitCode, error);
12397
+ },
12398
+ async fail(rawError) {
12399
+ if (!started || terminal)
12400
+ return;
12401
+ terminal = true;
12402
+ const error = sanitizeError(rawError);
12403
+ const exitCode = error.exitCode ?? 1;
12404
+ const result = validationFailure(exitCode, error) ? "validation_error" : "failure";
12405
+ await dispatch(result, exitCode, error);
12406
+ }
12407
+ };
12408
+ }
12409
+ var REQUEST_TIMEOUT_MS = 500, MAX_RESPONSE_BYTES, MAX_STACK_FRAMES = 20, UNKNOWN_COMMAND = "unknown", FALLBACK_INSTALL_ID = "00000000-0000-4000-8000-000000000000", BUNDLE_CODE_FILE = "app:///bin/dosu.js", CLI_PACKAGE_ROOT, SAFE_ERROR_TYPES, SAFE_ERROR_CODES;
12410
+ var init_telemetry = __esm(() => {
12411
+ MAX_RESPONSE_BYTES = 1024 * 1024;
12412
+ CLI_PACKAGE_ROOT = detectCliPackageRoot();
12413
+ SAFE_ERROR_TYPES = new Set([
12414
+ "AbortError",
12415
+ "AggregateError",
12416
+ "CommanderError",
12417
+ "CliUsageError",
12418
+ "CommandExitError",
12419
+ "Error",
12420
+ "OAuthCallbackError",
12421
+ "RangeError",
12422
+ "ReferenceError",
12423
+ "SessionExpiredError",
12424
+ "SyntaxError",
12425
+ "TicketHttpError",
12426
+ "TRPCClientError",
12427
+ "TypeError"
12428
+ ]);
12429
+ SAFE_ERROR_CODES = new Set([
12430
+ "ABORT_ERR",
12431
+ "BAD_GATEWAY",
12432
+ "BAD_REQUEST",
12433
+ "CLIENT_CLOSED_REQUEST",
12434
+ "CONFLICT",
12435
+ "EACCES",
12436
+ "EAI_AGAIN",
12437
+ "EBUSY",
12438
+ "ECONNREFUSED",
12439
+ "ECONNRESET",
12440
+ "EEXIST",
12441
+ "EHOSTUNREACH",
12442
+ "EINVAL",
12443
+ "EISDIR",
12444
+ "ELOOP",
12445
+ "EMFILE",
12446
+ "ENAMETOOLONG",
12447
+ "ENETUNREACH",
12448
+ "ENFILE",
12449
+ "ENOENT",
12450
+ "ENOMEM",
12451
+ "ENOSPC",
12452
+ "ENOTFOUND",
12453
+ "ENOTDIR",
12454
+ "EPIPE",
12455
+ "EPERM",
12456
+ "EROFS",
12457
+ "ETIMEDOUT",
12458
+ "FORBIDDEN",
12459
+ "GATEWAY_TIMEOUT",
12460
+ "IMPORT_ALREADY_IN_PROGRESS",
12461
+ "INTERNAL_SERVER_ERROR",
12462
+ "INVALID_ARGUMENT",
12463
+ "METHOD_NOT_SUPPORTED",
12464
+ "NOT_FOUND",
12465
+ "NOT_IMPLEMENTED",
12466
+ "PARSE_ERROR",
12467
+ "PAYLOAD_TOO_LARGE",
12468
+ "PAYMENT_REQUIRED",
12469
+ "PRECONDITION_FAILED",
12470
+ "SERVICE_UNAVAILABLE",
12471
+ "TIMEOUT",
12472
+ "TOO_MANY_REQUESTS",
12473
+ "UNAUTHORIZED",
12474
+ "UNPROCESSABLE_CONTENT",
12475
+ "UNSUPPORTED_MEDIA_TYPE",
12476
+ "VALIDATION_ERROR",
12477
+ "commander.conflictingOption",
12478
+ "commander.error",
12479
+ "commander.excessArguments",
12480
+ "commander.invalidArgument",
12481
+ "commander.missingArgument",
12482
+ "commander.missingMandatoryOptionValue",
12483
+ "commander.optionMissingArgument",
12484
+ "commander.unknownCommand",
12485
+ "commander.unknownOption"
12486
+ ]);
12487
+ });
12488
+
11556
12489
  // src/setup/analytics.ts
11557
12490
  async function trackCliOnboardingEvent(cfg, onboardingRunID, event, properties = {}) {
11558
- if (!cfg.active_account?.session.access_token)
11559
- return;
11560
12491
  try {
11561
- const trpc = createTypedClient(cfg);
11562
- await withTimeout(trpc.user.trackCliOnboardingEvent.mutate({
12492
+ if (!isTelemetryEnabled())
12493
+ return;
12494
+ if (!analyticsReleaseEnabled())
12495
+ return;
12496
+ if (!cfg.active_account?.session.access_token)
12497
+ return;
12498
+ if (!isUUID2(onboardingRunID))
12499
+ return;
12500
+ const input = {
11563
12501
  event,
11564
12502
  properties: {
11565
12503
  ...baseProperties(cfg),
11566
12504
  onboarding_run_id: onboardingRunID,
11567
- ...properties
12505
+ ...allowlistedWorkflowProperties(properties)
11568
12506
  }
11569
- }), TRACKING_TIMEOUT_MS);
12507
+ };
12508
+ if (printDebugPayload(input))
12509
+ return;
12510
+ await withTimeout((signal) => createAnalyticsClient(cfg.active_account?.session.access_token, signal).user.trackCliOnboardingEvent.mutate(input), TRACKING_TIMEOUT_MS);
11570
12511
  } catch (err) {
11571
- const msg = err instanceof Error ? err.message : String(err);
11572
- logger.debug("setup", `CLI onboarding analytics failed: ${event}: ${msg}`);
12512
+ logTrackingFailure("analytics", event, err);
11573
12513
  }
11574
12514
  }
11575
12515
  async function trackCliOnboardingPreAuthEvent(onboardingRunID, event, properties = {}) {
11576
12516
  try {
11577
- const trpc = createAnonymousClient();
11578
- await withTimeout(trpc.user.trackCliOnboardingPreAuthEvent.mutate({
12517
+ if (!isTelemetryEnabled())
12518
+ return;
12519
+ if (!analyticsReleaseEnabled())
12520
+ return;
12521
+ if (!isUUID2(onboardingRunID))
12522
+ return;
12523
+ const input = {
11579
12524
  event,
11580
12525
  onboarding_run_id: onboardingRunID,
11581
12526
  properties: {
11582
12527
  ...baseProperties(emptyConfig()),
11583
- ...properties
12528
+ ...allowlistedWorkflowProperties(properties)
11584
12529
  }
11585
- }), TRACKING_TIMEOUT_MS);
12530
+ };
12531
+ if (printDebugPayload(input))
12532
+ return;
12533
+ await withTimeout((signal) => createAnalyticsClient(undefined, signal).user.trackCliOnboardingPreAuthEvent.mutate(input), TRACKING_TIMEOUT_MS);
11586
12534
  } catch (err) {
11587
- const msg = err instanceof Error ? err.message : String(err);
11588
- logger.debug("setup", `CLI onboarding pre-auth analytics failed: ${event}: ${msg}`);
12535
+ logTrackingFailure("pre-auth analytics", event, err);
11589
12536
  }
11590
12537
  }
12538
+ function logTrackingFailure(kind, event, error) {
12539
+ try {
12540
+ const message = error instanceof Error ? error.message : String(error);
12541
+ logger.debug("setup", `CLI onboarding ${kind} failed: ${event}: ${message}`);
12542
+ } catch {}
12543
+ }
12544
+ function printDebugPayload(payload) {
12545
+ if (process.env.DOSU_TELEMETRY_DEBUG !== "1")
12546
+ return false;
12547
+ console.error(`[dosu telemetry:posthog] ${JSON.stringify(payload)}`);
12548
+ return true;
12549
+ }
12550
+ function analyticsReleaseEnabled() {
12551
+ return Boolean(parsePostHogProjectToken(process.env.DOSU_POSTHOG_PROJECT_TOKEN_OVERRIDE ?? "phc_Uo9RxhN0HRk8gZkPlT18BCr7eqsM0MPWVW88HZB95V5"));
12552
+ }
11591
12553
  function baseProperties(cfg) {
11592
12554
  return {
11593
- cli_version: VERSION,
11594
- platform: process.platform,
11595
- arch: process.arch,
12555
+ cli_version: safeIdentifier(VERSION, 32),
12556
+ install_channel: safeIdentifier(INSTALL_CHANNEL, 32),
12557
+ platform: safeIdentifier(process.platform, 24),
12558
+ arch: safeIdentifier(process.arch, 24),
11596
12559
  mode: cfg.mode ?? "cloud",
11597
- org_id: cfg.active_account?.target?.org_id,
11598
- deployment_id: cfg.active_account?.target?.deployment_id,
11599
- space_id: cfg.active_account?.target?.space_id
12560
+ org_id: safeIdentifier(cfg.active_account?.target?.org_id, 128),
12561
+ deployment_id: safeIdentifier(cfg.active_account?.target?.deployment_id, 128),
12562
+ space_id: safeIdentifier(cfg.active_account?.target?.space_id, 128)
11600
12563
  };
11601
12564
  }
11602
- function createAnonymousClient() {
11603
- const webAppURL = getWebAppURL();
12565
+ function allowlistedWorkflowProperties(properties) {
12566
+ const input = properties;
12567
+ const safe = {};
12568
+ if (typeof input.has_deployment_option === "boolean") {
12569
+ safe.has_deployment_option = input.has_deployment_option;
12570
+ }
12571
+ if (input.mode_option === "cloud" || input.mode_option === "oss") {
12572
+ safe.mode_option = input.mode_option;
12573
+ }
12574
+ if (input.flow_kind === "onboarding" || input.flow_kind === "oss" || input.flow_kind === "setup") {
12575
+ safe.flow_kind = input.flow_kind;
12576
+ }
12577
+ if (typeof input.reason === "string" && SETUP_FAILURE_REASONS.has(input.reason)) {
12578
+ safe.reason = input.reason;
12579
+ }
12580
+ if (typeof input.provider_count === "number" && Number.isInteger(input.provider_count) && input.provider_count >= 0 && input.provider_count <= 50) {
12581
+ safe.provider_count = input.provider_count;
12582
+ }
12583
+ if (Array.isArray(input.providers)) {
12584
+ safe.providers = input.providers.filter((provider) => typeof provider === "string" && /^[a-z][a-z0-9-]{0,31}$/.test(provider)).slice(0, 50);
12585
+ }
12586
+ for (const key of ["completed_mcp", "completed_skill", "completed_agents_md"]) {
12587
+ if (typeof input[key] === "boolean")
12588
+ safe[key] = input[key];
12589
+ }
12590
+ return safe;
12591
+ }
12592
+ function safeIdentifier(value, maxLength) {
12593
+ return typeof value === "string" && value.length <= maxLength && /^[A-Za-z0-9][A-Za-z0-9._+-]*$/.test(value) ? value : undefined;
12594
+ }
12595
+ function isUUID2(value) {
12596
+ return /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value);
12597
+ }
12598
+ function createAnalyticsClient(accessToken, requestSignal) {
12599
+ const webAppURL = parseTelemetryWebAppURL(getWebAppURL(), process.env.DOSU_DEV === "true");
11604
12600
  if (!webAppURL) {
11605
- throw new Error("Web app URL not configured");
12601
+ throw new Error("Secure web app URL not configured");
11606
12602
  }
11607
12603
  return createTRPCClient({
11608
12604
  links: [
@@ -11610,19 +12606,32 @@ function createAnonymousClient() {
11610
12606
  url: `${webAppURL}/api/cli-trpc`,
11611
12607
  transformer: dist_default,
11612
12608
  headers: {
11613
- "x-dosu-cli-contract": CLI_CONTRACT_HASH
12609
+ "x-dosu-cli-contract": CLI_CONTRACT_HASH,
12610
+ ...accessToken ? { "Supabase-Access-Token": accessToken } : {}
12611
+ },
12612
+ fetch: (url, options) => {
12613
+ const target = typeof url === "string" ? url : url instanceof URL ? url.toString() : url.url;
12614
+ return fetchWithoutRedirect(target, {
12615
+ ...options,
12616
+ redirect: "error",
12617
+ ...requestSignal ? { signal: requestSignal } : {}
12618
+ });
11614
12619
  }
11615
12620
  })
11616
12621
  ]
11617
12622
  });
11618
12623
  }
11619
- async function withTimeout(promise, timeoutMs) {
12624
+ async function withTimeout(operation, timeoutMs) {
12625
+ const controller = new AbortController;
11620
12626
  let timeout;
11621
12627
  try {
11622
12628
  return await Promise.race([
11623
- promise,
12629
+ operation(controller.signal),
11624
12630
  new Promise((_2, reject) => {
11625
- timeout = setTimeout(() => reject(new Error("tracking timeout")), timeoutMs);
12631
+ timeout = setTimeout(() => {
12632
+ controller.abort();
12633
+ reject(new Error("tracking timeout"));
12634
+ }, timeoutMs);
11626
12635
  })
11627
12636
  ]);
11628
12637
  } finally {
@@ -11630,13 +12639,32 @@ async function withTimeout(promise, timeoutMs) {
11630
12639
  clearTimeout(timeout);
11631
12640
  }
11632
12641
  }
11633
- var TRACKING_TIMEOUT_MS = 1500;
12642
+ var SETUP_FAILURE_REASONS, TRACKING_TIMEOUT_MS = 500;
11634
12643
  var init_analytics = __esm(() => {
11635
12644
  init_dist();
11636
12645
  init_dist4();
11637
- init_trpc();
11638
12646
  init_config();
11639
12647
  init_logger();
12648
+ init_settings();
12649
+ init_telemetry();
12650
+ SETUP_FAILURE_REASONS = new Set([
12651
+ "access_denied",
12652
+ "api_key_failed",
12653
+ "bad_oauth_state",
12654
+ "cloud_setup_context_failed",
12655
+ "deployment_resolution_failed",
12656
+ "invalid_request",
12657
+ "invalid_scope",
12658
+ "mcp_selection_cancelled",
12659
+ "oauth_callback_error",
12660
+ "onboarding_incomplete_after_handshake",
12661
+ "server_error",
12662
+ "temporarily_unavailable",
12663
+ "unauthorized_client",
12664
+ "unexpected_auth_error",
12665
+ "unsupported_response_type",
12666
+ "web_onboarding_incomplete"
12667
+ ]);
11640
12668
  });
11641
12669
 
11642
12670
  // src/setup/audit-handoff.ts
@@ -11778,34 +12806,6 @@ var init_rules_step = __esm(() => {
11778
12806
  init_styles();
11779
12807
  });
11780
12808
 
11781
- // src/auth/errors.ts
11782
- var exports_errors = {};
11783
- __export(exports_errors, {
11784
- OAuthCallbackError: () => OAuthCallbackError
11785
- });
11786
- var OAuthCallbackError;
11787
- var init_errors = __esm(() => {
11788
- OAuthCallbackError = class OAuthCallbackError extends Error {
11789
- error;
11790
- errorCode;
11791
- errorDescription;
11792
- constructor(message, details = {}) {
11793
- super(message);
11794
- this.name = "OAuthCallbackError";
11795
- this.error = details.error;
11796
- this.errorCode = details.errorCode;
11797
- this.errorDescription = details.errorDescription;
11798
- }
11799
- get userMessage() {
11800
- const desc = this.errorDescription ?? this.message;
11801
- if (this.errorCode === "bad_oauth_state" || /state/i.test(desc)) {
11802
- return `Authentication failed: ${desc}. Run \`dosu login\` again.`;
11803
- }
11804
- return `Authentication failed: ${desc}`;
11805
- }
11806
- };
11807
- });
11808
-
11809
12809
  // src/auth/server.ts
11810
12810
  function escapeHtml(value) {
11811
12811
  return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
@@ -11983,8 +12983,8 @@ h1 {
11983
12983
  async function startCallbackServer(opts = {}) {
11984
12984
  let resolveToken;
11985
12985
  let rejectToken;
11986
- const tokenPromise = new Promise((resolve, reject) => {
11987
- resolveToken = resolve;
12986
+ const tokenPromise = new Promise((resolve2, reject) => {
12987
+ resolveToken = resolve2;
11988
12988
  rejectToken = reject;
11989
12989
  });
11990
12990
  const http = __require("node:http");
@@ -12042,8 +13042,8 @@ async function startCallbackServer(opts = {}) {
12042
13042
  res.writeHead(200, { "Content-Type": "text/html" });
12043
13043
  res.end(buildSuccessHtml(email ?? undefined, opts.successVariant ?? "auth"));
12044
13044
  });
12045
- await new Promise((resolve) => {
12046
- httpServer.listen(0, "localhost", () => resolve());
13045
+ await new Promise((resolve2) => {
13046
+ httpServer.listen(0, "localhost", () => resolve2());
12047
13047
  });
12048
13048
  httpServer.unref();
12049
13049
  const addr = httpServer.address();
@@ -12213,17 +13213,21 @@ __export(exports_flow2, {
12213
13213
  stepDetectTools: () => stepDetectTools,
12214
13214
  stepConfigureTools: () => stepConfigureTools,
12215
13215
  runSetup: () => runSetup,
12216
- runInstallSkill: () => runInstallSkill
13216
+ runInstallSkill: () => runInstallSkill,
13217
+ cliAuthFailureReason: () => cliAuthFailureReason
12217
13218
  });
12218
- import { randomUUID } from "node:crypto";
13219
+ import { randomUUID as randomUUID3 } from "node:crypto";
13220
+ function trackInBackground(tracking) {
13221
+ tracking.catch(() => {});
13222
+ }
12219
13223
  async function runSetup(opts = {}) {
12220
- const onboardingRunID = randomUUID();
13224
+ const onboardingRunID = randomUUID3();
12221
13225
  logger.info("setup", `Setup flow started${opts.deploymentID ? ` deployment=${opts.deploymentID}` : ""}${opts.mode ? ` mode=${opts.mode}` : ""}`);
12222
13226
  intro("Dosu CLI Setup");
12223
- await trackCliOnboardingPreAuthEvent(onboardingRunID, "cli_onboarding_launch_attempted", {
13227
+ trackInBackground(trackCliOnboardingPreAuthEvent(onboardingRunID, "cli_onboarding_launch_attempted", {
12224
13228
  has_deployment_option: Boolean(opts.deploymentID),
12225
13229
  mode_option: opts.mode
12226
- });
13230
+ }));
12227
13231
  let cfg = loadConfig();
12228
13232
  applyModeOverride(cfg, opts);
12229
13233
  if (opts.deploymentID) {
@@ -12234,7 +13238,7 @@ async function runSetup(opts = {}) {
12234
13238
  if (!authed)
12235
13239
  return;
12236
13240
  cfg = authed.cfg;
12237
- await trackCliOnboardingEvent(cfg, onboardingRunID, "cli_onboarding_auth_completed");
13241
+ trackInBackground(trackCliOnboardingEvent(cfg, onboardingRunID, "cli_onboarding_auth_completed"));
12238
13242
  let apiClient = new Client(cfg);
12239
13243
  let cloudSetupContext = null;
12240
13244
  if (cfg.mode !== MODE_OSS) {
@@ -12242,39 +13246,39 @@ async function runSetup(opts = {}) {
12242
13246
  s.start("Loading your workspace...");
12243
13247
  cloudSetupContext = await resolveCloudSetupContext(cfg);
12244
13248
  if (!cloudSetupContext) {
12245
- await trackCliOnboardingEvent(cfg, onboardingRunID, "cli_onboarding_failed", {
13249
+ trackInBackground(trackCliOnboardingEvent(cfg, onboardingRunID, "cli_onboarding_failed", {
12246
13250
  reason: "cloud_setup_context_failed"
12247
- });
13251
+ }));
12248
13252
  s.stop("Workspace load failed");
12249
13253
  return;
12250
13254
  }
12251
13255
  s.stop("Workspace loaded");
12252
13256
  }
12253
- await trackCliOnboardingEvent(cfg, onboardingRunID, "cli_onboarding_started", {
13257
+ trackInBackground(trackCliOnboardingEvent(cfg, onboardingRunID, "cli_onboarding_started", {
12254
13258
  flow_kind: cloudSetupContext ? cloudSetupContext.finishedOnboarding ? "setup" : "onboarding" : "oss"
12255
- });
13259
+ }));
12256
13260
  const needsHandshake = cfg.mode !== MODE_OSS && cloudSetupContext !== null && !cloudSetupContext.finishedOnboarding && !opts.deploymentID;
12257
13261
  if (needsHandshake) {
12258
13262
  const handshook = await stepSetupHandshake(cfg, onboardingRunID);
12259
13263
  if (!handshook) {
12260
- await trackCliOnboardingEvent(cfg, onboardingRunID, "cli_onboarding_failed", {
13264
+ trackInBackground(trackCliOnboardingEvent(cfg, onboardingRunID, "cli_onboarding_failed", {
12261
13265
  reason: "web_onboarding_incomplete"
12262
- });
13266
+ }));
12263
13267
  return;
12264
13268
  }
12265
13269
  apiClient = new Client(cfg);
12266
13270
  const refreshed = await resolveCloudSetupContext(cfg);
12267
13271
  if (refreshed === null) {
12268
- await trackCliOnboardingEvent(cfg, onboardingRunID, "cli_onboarding_failed", {
13272
+ trackInBackground(trackCliOnboardingEvent(cfg, onboardingRunID, "cli_onboarding_failed", {
12269
13273
  reason: "cloud_setup_context_failed"
12270
- });
13274
+ }));
12271
13275
  return;
12272
13276
  }
12273
13277
  if (!refreshed.finishedOnboarding) {
12274
13278
  log.warn("Your account still needs onboarding. Finish it in the browser at the Dosu app, then re-run `dosu setup`.");
12275
- await trackCliOnboardingEvent(cfg, onboardingRunID, "cli_onboarding_failed", {
13279
+ trackInBackground(trackCliOnboardingEvent(cfg, onboardingRunID, "cli_onboarding_failed", {
12276
13280
  reason: "onboarding_incomplete_after_handshake"
12277
- });
13281
+ }));
12278
13282
  process.exitCode = 1;
12279
13283
  return;
12280
13284
  }
@@ -12283,43 +13287,43 @@ async function runSetup(opts = {}) {
12283
13287
  if (!cfg.active_account?.target?.deployment_id || opts.deploymentID) {
12284
13288
  const ok = await resolveDeployment(apiClient, cfg, opts);
12285
13289
  if (!ok) {
12286
- await trackCliOnboardingEvent(cfg, onboardingRunID, "cli_onboarding_failed", {
13290
+ trackInBackground(trackCliOnboardingEvent(cfg, onboardingRunID, "cli_onboarding_failed", {
12287
13291
  reason: "deployment_resolution_failed"
12288
- });
13292
+ }));
12289
13293
  return;
12290
13294
  }
12291
13295
  }
12292
13296
  const apiKey = await stepMintAPIKey(apiClient, cfg);
12293
13297
  if (!apiKey) {
12294
- await trackCliOnboardingEvent(cfg, onboardingRunID, "cli_onboarding_failed", {
13298
+ trackInBackground(trackCliOnboardingEvent(cfg, onboardingRunID, "cli_onboarding_failed", {
12295
13299
  reason: "api_key_failed"
12296
- });
13300
+ }));
12297
13301
  return;
12298
13302
  }
12299
13303
  updateTarget(cfg, { api_key: apiKey });
12300
13304
  saveConfig(cfg);
12301
13305
  const configured = await stepConfigureMcpTools(cfg);
12302
13306
  if (configured === null) {
12303
- await trackCliOnboardingEvent(cfg, onboardingRunID, "cli_onboarding_cancelled", {
13307
+ trackInBackground(trackCliOnboardingEvent(cfg, onboardingRunID, "cli_onboarding_cancelled", {
12304
13308
  reason: "mcp_selection_cancelled"
12305
- });
13309
+ }));
12306
13310
  return;
12307
13311
  }
12308
13312
  const mcpConfiguredThisRun = configured.some((result) => result.action === "install" || result.action === "skip");
12309
13313
  const configuredProviders = configured.filter((result) => (result.action === "install" || result.action === "skip") && !result.error);
12310
13314
  const mcpCompleted = configuredProviders.length > 0;
12311
13315
  if (mcpCompleted) {
12312
- await trackCliOnboardingEvent(cfg, onboardingRunID, "cli_onboarding_mcp_configured", {
13316
+ trackInBackground(trackCliOnboardingEvent(cfg, onboardingRunID, "cli_onboarding_mcp_configured", {
12313
13317
  provider_count: configuredProviders.length,
12314
13318
  providers: configuredProviders.map((result) => result.provider.id())
12315
- });
13319
+ }));
12316
13320
  }
12317
13321
  let skillCompleted = false;
12318
13322
  const skillProviders = configuredProviders.map((result) => result.provider).filter((provider) => skillInstallTargetForProvider(provider.id()) !== null);
12319
13323
  if (skillProviders.length > 0) {
12320
13324
  skillCompleted = await runInstallSkill(skillProviders);
12321
13325
  if (skillCompleted) {
12322
- await trackCliOnboardingEvent(cfg, onboardingRunID, "cli_onboarding_skill_installed");
13326
+ trackInBackground(trackCliOnboardingEvent(cfg, onboardingRunID, "cli_onboarding_skill_installed"));
12323
13327
  }
12324
13328
  }
12325
13329
  let agentsMdCompleted = false;
@@ -12331,11 +13335,11 @@ async function runSetup(opts = {}) {
12331
13335
  handoffToAudit = await offerAuditHandoff();
12332
13336
  }
12333
13337
  if (mcpCompleted || skillCompleted || agentsMdCompleted) {
12334
- await trackCliOnboardingEvent(cfg, onboardingRunID, "cli_onboarding_completed", {
13338
+ trackInBackground(trackCliOnboardingEvent(cfg, onboardingRunID, "cli_onboarding_completed", {
12335
13339
  completed_mcp: mcpCompleted,
12336
13340
  completed_skill: skillCompleted,
12337
13341
  completed_agents_md: agentsMdCompleted
12338
- });
13342
+ }));
12339
13343
  }
12340
13344
  if (cfg.mode === MODE_OSS) {
12341
13345
  outro("Setup complete! Using open-source libraries only.\n\nTips: Run `dosu setup --mode cloud` to connect your own repos.");
@@ -12447,7 +13451,7 @@ async function stepAuthenticate(existingCfg, onboardingRunID) {
12447
13451
  }
12448
13452
  }
12449
13453
  if (onboardingRunID) {
12450
- await trackCliOnboardingPreAuthEvent(onboardingRunID, "cli_onboarding_auth_started");
13454
+ trackInBackground(trackCliOnboardingPreAuthEvent(onboardingRunID, "cli_onboarding_auth_started"));
12451
13455
  }
12452
13456
  return await openBrowserForSetup(cfg, onboardingRunID);
12453
13457
  }
@@ -12484,25 +13488,30 @@ async function openBrowserForSetup(cfg, onboardingRunID) {
12484
13488
  } catch (err) {
12485
13489
  const msg = err instanceof Error ? err.stack ?? err.message : String(err);
12486
13490
  logger.error("setup", `Auth failed: ${msg}`);
12487
- const { OAuthCallbackError: OAuthCallbackError2 } = await Promise.resolve().then(() => (init_errors(), exports_errors));
12488
- if (err instanceof OAuthCallbackError2) {
13491
+ if (err instanceof OAuthCallbackError) {
12489
13492
  log.error(err.userMessage);
12490
13493
  if (onboardingRunID) {
12491
- await trackCliOnboardingPreAuthEvent(onboardingRunID, "cli_onboarding_auth_failed", {
12492
- reason: err.errorCode ?? err.errorDescription ?? "oauth_callback_error"
12493
- });
13494
+ trackInBackground(trackCliOnboardingPreAuthEvent(onboardingRunID, "cli_onboarding_auth_failed", {
13495
+ reason: cliAuthFailureReason(err)
13496
+ }));
12494
13497
  }
12495
13498
  return null;
12496
13499
  }
12497
13500
  log.error(`Authentication failed: ${err instanceof Error ? err.message : String(err)}`);
12498
13501
  if (onboardingRunID) {
12499
- await trackCliOnboardingPreAuthEvent(onboardingRunID, "cli_onboarding_auth_failed", {
12500
- reason: err instanceof Error ? err.message : String(err)
12501
- });
13502
+ trackInBackground(trackCliOnboardingPreAuthEvent(onboardingRunID, "cli_onboarding_auth_failed", {
13503
+ reason: cliAuthFailureReason(err)
13504
+ }));
12502
13505
  }
12503
13506
  return null;
12504
13507
  }
12505
13508
  }
13509
+ function cliAuthFailureReason(err) {
13510
+ if (!(err instanceof OAuthCallbackError))
13511
+ return "unexpected_auth_error";
13512
+ const code = err.errorCode;
13513
+ return code && OAUTH_ANALYTICS_REASONS.has(code) ? code : "oauth_callback_error";
13514
+ }
12506
13515
  async function stepSetupHandshake(cfg, onboardingRunID) {
12507
13516
  logger.info("setup", "Step: setup handshake");
12508
13517
  log.info("Almost there — finish setting up in the browser and we'll pick up from here.");
@@ -12800,9 +13809,10 @@ function stepShowSummary(results) {
12800
13809
  log.success("All agents already configured. No changes needed.");
12801
13810
  }
12802
13811
  }
12803
- var SETUP_HANDSHAKE_TIMEOUT_MS = 1800000;
13812
+ var OAUTH_ANALYTICS_REASONS, SETUP_HANDSHAKE_TIMEOUT_MS = 1800000;
12804
13813
  var init_flow2 = __esm(() => {
12805
13814
  init_dist8();
13815
+ init_errors();
12806
13816
  init_client();
12807
13817
  init_skill();
12808
13818
  init_config();
@@ -12813,11 +13823,21 @@ var init_flow2 = __esm(() => {
12813
13823
  init_audit_handoff();
12814
13824
  init_rules_step();
12815
13825
  init_styles();
13826
+ OAUTH_ANALYTICS_REASONS = new Set([
13827
+ "access_denied",
13828
+ "bad_oauth_state",
13829
+ "invalid_request",
13830
+ "invalid_scope",
13831
+ "server_error",
13832
+ "temporarily_unavailable",
13833
+ "unauthorized_client",
13834
+ "unsupported_response_type"
13835
+ ]);
12816
13836
  });
12817
13837
 
12818
13838
  // src/commands/insights.ts
12819
- import { existsSync as existsSync18, mkdirSync as mkdirSync9, readdirSync, unlinkSync as unlinkSync2, writeFileSync as writeFileSync9 } from "node:fs";
12820
- import { dirname as dirname7, join as join29 } from "node:path";
13839
+ import { existsSync as existsSync20, mkdirSync as mkdirSync10, readdirSync, unlinkSync as unlinkSync3, writeFileSync as writeFileSync10 } from "node:fs";
13840
+ import { dirname as dirname7, join as join30 } from "node:path";
12821
13841
  function shuffled(arr) {
12822
13842
  const copy2 = [...arr];
12823
13843
  for (let i2 = copy2.length - 1;i2 > 0; i2--) {
@@ -12921,17 +13941,17 @@ function makeAskFn(cfg) {
12921
13941
  };
12922
13942
  }
12923
13943
  function insightsDir() {
12924
- return join29(getConfigDir(), "insights");
13944
+ return join30(getConfigDir(), "insights");
12925
13945
  }
12926
13946
  function reportPath(timestamp) {
12927
13947
  const dir = insightsDir();
12928
13948
  if (!timestamp)
12929
- return join29(dir, "latest.html");
13949
+ return join30(dir, "latest.html");
12930
13950
  const iso = timestamp.toISOString().slice(0, 19).replace(/:/g, "-");
12931
- return join29(dir, `report-${iso}Z.html`);
13951
+ return join30(dir, `report-${iso}Z.html`);
12932
13952
  }
12933
13953
  function pruneOldReports(dir, keepN) {
12934
- if (!existsSync18(dir))
13954
+ if (!existsSync20(dir))
12935
13955
  return;
12936
13956
  let entries;
12937
13957
  try {
@@ -12943,7 +13963,7 @@ function pruneOldReports(dir, keepN) {
12943
13963
  const reports = entries.filter((f2) => REPORT_FILE_PATTERN.test(f2)).sort().reverse();
12944
13964
  for (const f2 of reports.slice(keepN)) {
12945
13965
  try {
12946
- unlinkSync2(join29(dir, f2));
13966
+ unlinkSync3(join30(dir, f2));
12947
13967
  } catch (err) {
12948
13968
  logger.debug("insights", `failed to prune ${f2}: ${err instanceof Error ? err.message : err}`);
12949
13969
  }
@@ -13016,9 +14036,9 @@ function defaultRunner(cfg) {
13016
14036
  render: renderHTML,
13017
14037
  writeFile: (path3, content) => {
13018
14038
  const dir = dirname7(path3);
13019
- if (!existsSync18(dir))
13020
- mkdirSync9(dir, { recursive: true, mode: 448 });
13021
- writeFileSync9(path3, content, { mode: 384 });
14039
+ if (!existsSync20(dir))
14040
+ mkdirSync10(dir, { recursive: true, mode: 448 });
14041
+ writeFileSync10(path3, content, { mode: 384 });
13022
14042
  },
13023
14043
  prune: pruneOldReports,
13024
14044
  openInBrowser: async (path3) => {
@@ -13442,7 +14462,7 @@ Could not open a browser automatically.`);
13442
14462
  throw new Error("Authentication timed out. Run 'dosu login' to try again.");
13443
14463
  }
13444
14464
  function sleep5(ms) {
13445
- return new Promise((resolve) => setTimeout(resolve, ms));
14465
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
13446
14466
  }
13447
14467
  var POLL_INTERVAL_MS = 5000;
13448
14468
  var init_device = __esm(() => {
@@ -13811,7 +14831,15 @@ var init_flow3 = __esm(() => {
13811
14831
  // src/cli/cli.ts
13812
14832
  init_commander();
13813
14833
  init_client();
13814
- import { readFileSync as readFileSync18, unlinkSync as unlinkSync3 } from "node:fs";
14834
+ import {
14835
+ closeSync,
14836
+ constants,
14837
+ fstatSync,
14838
+ openSync,
14839
+ readFileSync as readFileSync19,
14840
+ readSync,
14841
+ unlinkSync as unlinkSync4
14842
+ } from "node:fs";
13815
14843
 
13816
14844
  // src/commands/analytics.ts
13817
14845
  init_commander();
@@ -15986,7 +17014,7 @@ init_dist();
15986
17014
  init_commander();
15987
17015
  init_trpc();
15988
17016
  var import_picocolors19 = __toESM(require_picocolors(), 1);
15989
- import { readFileSync as readFileSync16 } from "node:fs";
17017
+ import { readFileSync as readFileSync17 } from "node:fs";
15990
17018
  function requireConfig10() {
15991
17019
  return requireLoginConfig();
15992
17020
  }
@@ -16162,7 +17190,7 @@ function reviewCommand() {
16162
17190
  let body = opts.body;
16163
17191
  if (opts.bodyFile) {
16164
17192
  try {
16165
- body = readFileSync16(opts.bodyFile, "utf-8");
17193
+ body = readFileSync17(opts.bodyFile, "utf-8");
16166
17194
  } catch (err) {
16167
17195
  console.error(import_picocolors19.default.red(`Failed to read --body-file: ${err.message}`));
16168
17196
  process.exit(1);
@@ -16480,6 +17508,71 @@ function suggestCommand() {
16480
17508
  return cmd;
16481
17509
  }
16482
17510
 
17511
+ // src/commands/telemetry.ts
17512
+ init_commander();
17513
+ init_settings();
17514
+ init_telemetry();
17515
+ function statusPayload() {
17516
+ const settings2 = loadTelemetrySettings();
17517
+ const environmentOverride = telemetryDisabledByEnvironment();
17518
+ const webAppURL = getWebAppURL();
17519
+ const commandWebAppURL = parseTelemetryWebAppURL(webAppURL);
17520
+ const setupWebAppURL = parseTelemetryWebAppURL(webAppURL, process.env.DOSU_DEV === "true");
17521
+ const analyticsReleaseToken = parsePostHogProjectToken(process.env.DOSU_POSTHOG_PROJECT_TOKEN_OVERRIDE ?? "phc_Uo9RxhN0HRk8gZkPlT18BCr7eqsM0MPWVW88HZB95V5");
17522
+ return {
17523
+ schema_version: settings2.schema_version,
17524
+ enabled: isTelemetryEnabled(settings2),
17525
+ destinations: {
17526
+ command_analytics: Boolean(commandWebAppURL && analyticsReleaseToken),
17527
+ setup_analytics: Boolean(setupWebAppURL && analyticsReleaseToken),
17528
+ error_diagnostics: Boolean(parseSentryDsn(process.env.DOSU_CLI_SENTRY_DSN_OVERRIDE ?? "https://f6b3e51dea6f50746316aa1896df76e0@o4505364738670592.ingest.us.sentry.io/4511883653283840"))
17529
+ },
17530
+ environment_override: environmentOverride ?? null,
17531
+ debug_mode: process.env.DOSU_TELEMETRY_DEBUG === "1",
17532
+ telemetry_id: settings2.install_id ?? null
17533
+ };
17534
+ }
17535
+ function printHumanStatus() {
17536
+ const status = statusPayload();
17537
+ console.log(`Telemetry: ${status.enabled ? "enabled" : "disabled"}`);
17538
+ console.log(`Destinations: command analytics PostHog ${status.destinations.command_analytics ? "configured" : "not configured"}, setup analytics Dosu API ${status.destinations.setup_analytics ? "configured" : "not configured"}, error diagnostics Sentry ${status.destinations.error_diagnostics ? "configured" : "not configured"}`);
17539
+ if (status.environment_override) {
17540
+ console.log(`Environment override: ${status.environment_override}`);
17541
+ }
17542
+ if (status.debug_mode)
17543
+ console.log("Debug mode: on (payloads are printed to stderr, not sent)");
17544
+ if (status.telemetry_id)
17545
+ console.log(`Telemetry ID: ${status.telemetry_id}`);
17546
+ console.log("Telemetry fields never include raw command lines, free-form arguments or option values, user source files, local paths, credentials, error messages, or debug.log; setup analytics uses only documented coarse fields and its existing Dosu session header only for API authorization.");
17547
+ }
17548
+ function updateTelemetry(enabled) {
17549
+ if (!setTelemetryEnabled(enabled)) {
17550
+ throw new Error("Could not save telemetry settings");
17551
+ }
17552
+ console.log(`Telemetry ${enabled ? "enabled" : "disabled"}.`);
17553
+ }
17554
+ function telemetryCommand() {
17555
+ const command = new Command("telemetry").description("Manage privacy-preserving telemetry");
17556
+ command.command("status").description("Show telemetry state and destination configuration").option("--json", "Output as JSON").action((opts) => {
17557
+ if (opts.json) {
17558
+ console.log(JSON.stringify(statusPayload(), null, 2));
17559
+ return;
17560
+ }
17561
+ printHumanStatus();
17562
+ });
17563
+ command.command("enable").description("Enable telemetry").action(() => updateTelemetry(true));
17564
+ command.command("disable").description("Disable telemetry").action(() => updateTelemetry(false));
17565
+ command.command("reset").description("Rotate the local pseudonymous telemetry ID (does not delete prior events)").action(() => {
17566
+ const previous = loadTelemetrySettings().install_id;
17567
+ const next = previous ? resetInstallID() : getOrCreateInstallID();
17568
+ if (!next)
17569
+ throw new Error("Could not save telemetry settings");
17570
+ console.log(`Telemetry ID ${previous ? "rotated" : "created"}: ${next}`);
17571
+ console.log("This does not delete events already retained by telemetry providers.");
17572
+ });
17573
+ return command;
17574
+ }
17575
+
16483
17576
  // src/commands/threads.ts
16484
17577
  init_commander();
16485
17578
  init_trpc();
@@ -16765,8 +17858,8 @@ function owningPackageManager(entrypoint, platform4, comSpec, env = process.env,
16765
17858
  }
16766
17859
  const owners = Object.keys(PACKAGE_MANAGERS).filter((manager) => packageRoots(manager, platform4, comSpec, env, cwd).some((root) => {
16767
17860
  try {
16768
- const relative = path3.relative(realpathSync(root), realEntrypoint);
16769
- return relative !== ".." && !relative.startsWith(`..${path3.sep}`) && !path3.isAbsolute(relative);
17861
+ const relative2 = path3.relative(realpathSync(root), realEntrypoint);
17862
+ return relative2 !== ".." && !relative2.startsWith(`..${path3.sep}`) && !path3.isAbsolute(relative2);
16770
17863
  } catch {
16771
17864
  return false;
16772
17865
  }
@@ -16860,14 +17953,16 @@ init_config();
16860
17953
  init_logger();
16861
17954
  init_providers();
16862
17955
  init_styles();
17956
+ init_settings();
17957
+ init_telemetry();
16863
17958
  init_skill_update_check();
16864
17959
 
16865
17960
  // src/version/update-check.ts
16866
17961
  init_config();
16867
17962
  init_logger();
16868
17963
  var import_picocolors25 = __toESM(require_picocolors(), 1);
16869
- import { existsSync as existsSync19, mkdirSync as mkdirSync10, readFileSync as readFileSync17, writeFileSync as writeFileSync10 } from "node:fs";
16870
- import { join as join30 } from "node:path";
17964
+ import { existsSync as existsSync21, mkdirSync as mkdirSync11, readFileSync as readFileSync18, writeFileSync as writeFileSync11 } from "node:fs";
17965
+ import { join as join31 } from "node:path";
16871
17966
  var CACHE_FILENAME3 = "update-check.json";
16872
17967
  var CHECK_INTERVAL_MS3 = 6 * 60 * 60 * 1000;
16873
17968
  var FETCH_TIMEOUT_MS3 = 1000;
@@ -16894,14 +17989,14 @@ function isNewerVersion(latest, current) {
16894
17989
  return false;
16895
17990
  }
16896
17991
  function getCachePath3() {
16897
- return join30(getConfigDir(), CACHE_FILENAME3);
17992
+ return join31(getConfigDir(), CACHE_FILENAME3);
16898
17993
  }
16899
17994
  function readCache() {
16900
17995
  try {
16901
17996
  const path3 = getCachePath3();
16902
- if (!existsSync19(path3))
17997
+ if (!existsSync21(path3))
16903
17998
  return null;
16904
- const data = JSON.parse(readFileSync17(path3, "utf-8"));
17999
+ const data = JSON.parse(readFileSync18(path3, "utf-8"));
16905
18000
  if (typeof data.lastCheck === "number" && isValidVersion(data.latestVersion)) {
16906
18001
  return data;
16907
18002
  }
@@ -16913,10 +18008,10 @@ function readCache() {
16913
18008
  function writeCache(cache) {
16914
18009
  try {
16915
18010
  const dir = getConfigDir();
16916
- if (!existsSync19(dir)) {
16917
- mkdirSync10(dir, { recursive: true, mode: 448 });
18011
+ if (!existsSync21(dir)) {
18012
+ mkdirSync11(dir, { recursive: true, mode: 448 });
16918
18013
  }
16919
- writeFileSync10(getCachePath3(), JSON.stringify(cache), { mode: 384 });
18014
+ writeFileSync11(getCachePath3(), JSON.stringify(cache), { mode: 384 });
16920
18015
  } catch {}
16921
18016
  }
16922
18017
  async function fetchLatestVersion() {
@@ -17008,6 +18103,104 @@ function isHookEntrypointInvocation(argv) {
17008
18103
  function shouldRunBackgroundChecks(actionName, argv) {
17009
18104
  return actionName !== "upgrade" && !isHookEntrypointInvocation(argv);
17010
18105
  }
18106
+ var TELEMETRY_EXCLUDED_COMMANDS = new Set([
18107
+ "hooks user-prompt-submit",
18108
+ "hooks post-tool-use",
18109
+ "hooks stop"
18110
+ ]);
18111
+ var TELEMETRY_FLUSH_TIMEOUT_MS = 750;
18112
+ var MAX_TELEMETRY_CONFIG_BYTES = 64 * 1024;
18113
+
18114
+ class CliUsageError extends Error {
18115
+ exitCode = 1;
18116
+ constructor(message) {
18117
+ super(message);
18118
+ this.name = "CliUsageError";
18119
+ }
18120
+ }
18121
+ function commandTelemetryName(actionCommand) {
18122
+ const segments = [];
18123
+ let current = actionCommand;
18124
+ while (current.parent) {
18125
+ segments.unshift(current.name());
18126
+ current = current.parent;
18127
+ }
18128
+ if (segments.length > 0)
18129
+ return segments.join(" ");
18130
+ return actionCommand.args.length > 0 ? "unknown" : "tui";
18131
+ }
18132
+ function shouldTrackCommand(command) {
18133
+ if (command === "telemetry" || command.startsWith("telemetry "))
18134
+ return false;
18135
+ return !TELEMETRY_EXCLUDED_COMMANDS.has(command);
18136
+ }
18137
+ function commandTelemetryContext() {
18138
+ try {
18139
+ const cfg = loadConfigForTelemetry();
18140
+ if (!cfg)
18141
+ return { mode: "cloud", isAuthenticated: false };
18142
+ const authenticated = isAuthenticated(cfg);
18143
+ const accessToken = authenticated ? cfg.active_account.session.access_token : "";
18144
+ const configUserID = getConfigUserID(cfg);
18145
+ const tokenUserID = getAccessTokenUserID(accessToken);
18146
+ const userID = configUserID && configUserID === tokenUserID ? configUserID : undefined;
18147
+ const email = userID ? getAccessTokenEmail(accessToken) : undefined;
18148
+ return {
18149
+ mode: cfg.mode === MODE_OSS ? "oss" : "cloud",
18150
+ isAuthenticated: authenticated,
18151
+ ...userID ? { user: { id: userID, ...email ? { email } : {} } } : {}
18152
+ };
18153
+ } catch {
18154
+ return { mode: "cloud", isAuthenticated: false };
18155
+ }
18156
+ }
18157
+ function loadConfigForTelemetry() {
18158
+ let fd;
18159
+ try {
18160
+ const nonblocking = typeof constants.O_NONBLOCK === "number" ? constants.O_NONBLOCK : 0;
18161
+ fd = openSync(getConfigPath(), constants.O_RDONLY | nonblocking);
18162
+ const file = fstatSync(fd);
18163
+ if (!file.isFile() || file.size > MAX_TELEMETRY_CONFIG_BYTES)
18164
+ return;
18165
+ const content = Buffer.alloc(MAX_TELEMETRY_CONFIG_BYTES + 1);
18166
+ const bytesRead = readSync(fd, content, 0, content.byteLength, 0);
18167
+ if (bytesRead > MAX_TELEMETRY_CONFIG_BYTES)
18168
+ return;
18169
+ return parseConfig(JSON.parse(content.subarray(0, bytesRead).toString("utf8")));
18170
+ } catch {
18171
+ return;
18172
+ } finally {
18173
+ if (fd !== undefined) {
18174
+ try {
18175
+ closeSync(fd);
18176
+ } catch {}
18177
+ }
18178
+ }
18179
+ }
18180
+ function startTelemetry(telemetry, command, context) {
18181
+ if (!telemetry)
18182
+ return false;
18183
+ try {
18184
+ telemetry.start(command, context);
18185
+ return true;
18186
+ } catch {
18187
+ return false;
18188
+ }
18189
+ }
18190
+ async function finishTelemetry(operation) {
18191
+ let timeout;
18192
+ try {
18193
+ await Promise.race([
18194
+ Promise.resolve().then(operation),
18195
+ new Promise((resolve2) => {
18196
+ timeout = setTimeout(resolve2, TELEMETRY_FLUSH_TIMEOUT_MS);
18197
+ })
18198
+ ]);
18199
+ } catch {} finally {
18200
+ if (timeout)
18201
+ clearTimeout(timeout);
18202
+ }
18203
+ }
17011
18204
  function suggestClosestCommand(input, program2) {
17012
18205
  let best;
17013
18206
  let bestDistance = Infinity;
@@ -17034,8 +18227,10 @@ function editDistance2(a3, b2) {
17034
18227
  }
17035
18228
  return prev[b2.length];
17036
18229
  }
17037
- function createProgram() {
18230
+ function createProgram(options = {}) {
17038
18231
  const program2 = new Command;
18232
+ let telemetryStarted = false;
18233
+ let telemetryValidationFailure = false;
17039
18234
  program2.name("dosu").description("Dosu CLI - Manage MCP servers for AI tools").version(getVersionString(), "-v, --version").helpCommand("help [command]", "Show help for a command").option("--debug", "Enable debug logging to stderr", false).hook("preAction", async (thisCommand, actionCommand) => {
17040
18235
  const opts = thisCommand.optsWithGlobals();
17041
18236
  logger.init({ debug: opts.debug });
@@ -17046,6 +18241,20 @@ function createProgram() {
17046
18241
  checkForSkillUpdates();
17047
18242
  checkForReadyTasks();
17048
18243
  }
18244
+ const command = commandTelemetryName(actionCommand);
18245
+ if (options.telemetry && shouldTrackCommand(command)) {
18246
+ telemetryStarted = startTelemetry(options.telemetry, command, commandTelemetryContext());
18247
+ }
18248
+ }).hook("postAction", async () => {
18249
+ const exitCode = Number(process.exitCode ?? 0);
18250
+ const telemetry = options.telemetry;
18251
+ if (telemetryStarted && telemetry) {
18252
+ if (telemetryValidationFailure) {
18253
+ await finishTelemetry(() => telemetry.fail(new CliUsageError("expected CLI usage error")));
18254
+ } else {
18255
+ await finishTelemetry(() => telemetry.complete(Number.isFinite(exitCode) ? exitCode : 1));
18256
+ }
18257
+ }
17049
18258
  }).allowExcessArguments(true).action(async () => {
17050
18259
  const command = program2.args[0];
17051
18260
  if (command !== undefined) {
@@ -17057,6 +18266,7 @@ function createProgram() {
17057
18266
  message += `
17058
18267
  Run 'dosu --help' to see available commands.`;
17059
18268
  console.error(message);
18269
+ telemetryValidationFailure = true;
17060
18270
  process.exitCode = 1;
17061
18271
  return;
17062
18272
  }
@@ -17218,20 +18428,20 @@ Run 'dosu --help' to see available commands.`;
17218
18428
  try {
17219
18429
  provider = getProvider(toolId.toLowerCase());
17220
18430
  } catch {
17221
- throw new Error(`unknown tool '${toolId}'. Use 'dosu mcp list' to see available tools`);
18431
+ throw new CliUsageError(`unknown tool '${toolId}'. Use 'dosu mcp list' to see available tools`);
17222
18432
  }
17223
18433
  const cfg = loadConfig();
17224
18434
  if (!isAuthenticated(cfg)) {
17225
- throw new Error("not logged in. Run 'dosu login' first");
18435
+ throw new CliUsageError("not logged in. Run 'dosu login' first");
17226
18436
  }
17227
18437
  if (isTokenExpired(cfg) && !await ensureFreshSession(cfg)) {
17228
- throw new Error("session expired. Run 'dosu login' to re-authenticate");
18438
+ throw new CliUsageError("session expired. Run 'dosu login' to re-authenticate");
17229
18439
  }
17230
18440
  if (cfg.mode !== MODE_OSS && !cfg.active_account?.target?.deployment_id) {
17231
- throw new Error("no MCP selected. Run 'dosu' to open the TUI and select an MCP");
18441
+ throw new CliUsageError("no MCP selected. Run 'dosu' to open the TUI and select an MCP");
17232
18442
  }
17233
18443
  if (!cfg.active_account?.target?.api_key) {
17234
- throw new Error("no API key available. Run 'dosu setup' to create one");
18444
+ throw new CliUsageError("no API key available. Run 'dosu setup' to create one");
17235
18445
  }
17236
18446
  if (provider.id() === "manual") {
17237
18447
  provider.install(cfg, false, { showSecret: opts.showSecret });
@@ -17284,6 +18494,7 @@ Use 'dosu mcp add <agent>' to add Dosu MCP to a tool.`);
17284
18494
  program2.addCommand(reviewCommand());
17285
18495
  program2.addCommand(sourcesCommand());
17286
18496
  program2.addCommand(suggestCommand());
18497
+ program2.addCommand(telemetryCommand());
17287
18498
  program2.addCommand(topicsCommand());
17288
18499
  program2.addCommand(threadsCommand());
17289
18500
  program2.addCommand(skillCommand());
@@ -17310,14 +18521,14 @@ Use 'dosu mcp add <agent>' to add Dosu MCP to a tool.`);
17310
18521
  return;
17311
18522
  }
17312
18523
  if (opts.tool || opts.loginTicket) {
17313
- throw new Error("--tool and --login-ticket require --agent");
18524
+ throw new CliUsageError("--tool and --login-ticket require --agent");
17314
18525
  }
17315
18526
  const { runSetup: runSetup2 } = await Promise.resolve().then(() => (init_flow2(), exports_flow2));
17316
18527
  let mode;
17317
18528
  if (opts.mode !== undefined) {
17318
18529
  const normalized = opts.mode.toLowerCase();
17319
18530
  if (normalized !== "oss" && normalized !== "cloud") {
17320
- throw new Error(`invalid --mode value '${opts.mode}' (expected 'oss' or 'cloud')`);
18531
+ throw new CliUsageError(`invalid --mode value '${opts.mode}' (expected 'oss' or 'cloud')`);
17321
18532
  }
17322
18533
  mode = normalized;
17323
18534
  }
@@ -17327,7 +18538,7 @@ Use 'dosu mcp add <agent>' to add Dosu MCP to a tool.`);
17327
18538
  const logPath = logger.getLogPath();
17328
18539
  if (opts.clear) {
17329
18540
  try {
17330
- unlinkSync3(logPath);
18541
+ unlinkSync4(logPath);
17331
18542
  console.log("Log file deleted.");
17332
18543
  } catch {
17333
18544
  console.log("No log file to delete.");
@@ -17337,7 +18548,7 @@ Use 'dosu mcp add <agent>' to add Dosu MCP to a tool.`);
17337
18548
  if (opts.tail !== undefined) {
17338
18549
  const n2 = typeof opts.tail === "string" ? parseInt(opts.tail, 10) || 50 : 50;
17339
18550
  try {
17340
- const content = readFileSync18(logPath, "utf-8");
18551
+ const content = readFileSync19(logPath, "utf-8");
17341
18552
  const lines = content.split(`
17342
18553
  `);
17343
18554
  console.log(lines.slice(-n2).join(`
@@ -17364,8 +18575,27 @@ async function ensureFreshSession(cfg) {
17364
18575
  }
17365
18576
  }
17366
18577
  async function execute() {
17367
- const program2 = createProgram();
17368
- await program2.parseAsync(process.argv);
18578
+ const telemetry = isHookEntrypointInvocation(process.argv) ? undefined : processCommandTelemetry();
18579
+ const program2 = createProgram({ telemetry });
18580
+ try {
18581
+ await program2.parseAsync(process.argv);
18582
+ } catch (err) {
18583
+ if (telemetry)
18584
+ await finishTelemetry(() => telemetry.fail(err));
18585
+ throw err;
18586
+ }
18587
+ }
18588
+ function processCommandTelemetry() {
18589
+ try {
18590
+ const settings2 = loadTelemetrySettings();
18591
+ if (!isTelemetryEnabled(settings2))
18592
+ return;
18593
+ return createCommandTelemetry({}, {
18594
+ resolveInstallId: () => settings2.install_id ?? getOrCreateInstallID()
18595
+ });
18596
+ } catch {
18597
+ return;
18598
+ }
17369
18599
  }
17370
18600
 
17371
18601
  // src/index.ts
@@ -17383,3 +18613,5 @@ execute().catch((err) => {
17383
18613
  }
17384
18614
  process.exit(1);
17385
18615
  });
18616
+
18617
+ //# debugId=df4adcb4-addf-31e4-6475-6e2164756e21