@togglhq/cli 1.11.82 → 1.11.83

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -282,7 +282,7 @@ Disable product analytics:
282
282
  TOGGL_PRODUCT_ANALYTICS=off
283
283
  ```
284
284
 
285
- Optional error reporting via Sentry is off unless you set `TOGGL_SENTRY=1` and `SENTRY_DSN`.
285
+ Error reporting is on by default. It sends a redacted error category, the stack trace with package-relative file paths, the CLI, Node and OS versions, and sampled request timings without query strings. It never sends command arguments, output, exception text, your hostname, local paths, or your project's dependency list. Disable it with `TOGGL_SENTRY=off`, or send it to your own Sentry project with `TOGGL_SENTRY_DSN`.
286
286
 
287
287
  ## License
288
288
 
package/build/cli.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { a as CommanderError, i as shutdownCliTelemetry, n as captureCliException, r as initCliTelemetry, t as createProgram } from "./program-DZlKCm-L.js";
2
+ import { a as CommanderError, i as shutdownCliTelemetry, n as captureCliException, r as initCliTelemetry, t as createProgram } from "./program-CZaDiFSf.js";
3
3
  import { readFileSync } from "node:fs";
4
4
  import { dirname, join } from "node:path";
5
5
  import { fileURLToPath } from "node:url";
package/build/index.js CHANGED
@@ -1,2 +1,2 @@
1
- import { t as createProgram } from "./program-DZlKCm-L.js";
1
+ import { t as createProgram } from "./program-CZaDiFSf.js";
2
2
  export { createProgram };
@@ -12672,6 +12672,120 @@ function redactUnsafeSentryMessages(event) {
12672
12672
  for (const value of event.exception?.values ?? []) if (!isSafeSentryMessage(value.value)) value.value = REDACTED_SENTRY_MESSAGE;
12673
12673
  return event;
12674
12674
  }
12675
+ /**
12676
+ * Shared Sentry setup for the public CLI and MCP packages. Reporting is on by
12677
+ * default there, so everything the SDK attaches on its own has to be safe to
12678
+ * send from any user's machine, not only the exception message. Both packages
12679
+ * build their `Sentry.init` options from here so the two cannot drift.
12680
+ */
12681
+ function normalized(raw) {
12682
+ const value = raw?.trim();
12683
+ return value ? value.toLowerCase() : void 0;
12684
+ }
12685
+ function isOff(raw) {
12686
+ const value = normalized(raw);
12687
+ return value === "0" || value === "false" || value === "off" || value === "no";
12688
+ }
12689
+ function isOn(raw) {
12690
+ const value = normalized(raw);
12691
+ return value === "1" || value === "true" || value === "on" || value === "yes";
12692
+ }
12693
+ /**
12694
+ * Picks the DSN the public build reports to, or `undefined` when reporting is
12695
+ * off.
12696
+ *
12697
+ * `TOGGL_SENTRY_DSN` is the override. The generic `SENTRY_DSN` is what most Node
12698
+ * apps set, and the MCP loads the working directory's `.env`, so on its own it
12699
+ * must not redirect Toggl's errors into the user's project. It is still honoured
12700
+ * with an explicit `TOGGL_SENTRY=1`, the old opt-in, and `SENTRY_DSN=off` still
12701
+ * switches reporting off.
12702
+ */
12703
+ function resolvePublicSentryDsn(builtIn, env = process.env) {
12704
+ if (isOff(env.TOGGL_SENTRY) || normalized(env.SENTRY_DSN) === "off") return void 0;
12705
+ const override = env.TOGGL_SENTRY_DSN?.trim();
12706
+ if (override) return override.toLowerCase() === "off" ? void 0 : override;
12707
+ const legacy = env.SENTRY_DSN?.trim();
12708
+ if (legacy && isOn(env.TOGGL_SENTRY)) return legacy;
12709
+ return builtIn;
12710
+ }
12711
+ /**
12712
+ * `/Users/jane/.npm/_npx/…/node_modules/@togglhq/mcp/build/cli.js` becomes
12713
+ * `@togglhq/mcp/build/cli.js`. A path outside `node_modules` keeps only its file
12714
+ * name, so no home directory or login leaves the machine.
12715
+ */
12716
+ function toPackageRelativePath(path) {
12717
+ const unified = path.replace(/^file:\/\//, "").replaceAll("\\", "/");
12718
+ const at = unified.lastIndexOf("/node_modules/");
12719
+ if (at !== -1) return unified.slice(at + 14);
12720
+ if (!unified.includes("/")) return unified;
12721
+ return unified.slice(unified.lastIndexOf("/") + 1);
12722
+ }
12723
+ function scrubStacktrace(stacktrace) {
12724
+ for (const frame of stacktrace?.frames ?? []) {
12725
+ if (typeof frame.filename === "string") frame.filename = toPackageRelativePath(frame.filename);
12726
+ if (typeof frame.abs_path === "string") frame.abs_path = toPackageRelativePath(frame.abs_path);
12727
+ }
12728
+ }
12729
+ const QUERY_ATTRIBUTES = [
12730
+ "http.query",
12731
+ "url.query",
12732
+ "http.fragment",
12733
+ "url.fragment"
12734
+ ];
12735
+ const URL_ATTRIBUTES = [
12736
+ "url.full",
12737
+ "http.url",
12738
+ "http.target",
12739
+ "url"
12740
+ ];
12741
+ function stripQuery(value) {
12742
+ return value.replace(/[?#]\S*/, "");
12743
+ }
12744
+ function scrubSpan(span) {
12745
+ if (!span) return;
12746
+ if (typeof span.description === "string") span.description = stripQuery(span.description);
12747
+ const data = span.data;
12748
+ if (!data) return;
12749
+ for (const key of QUERY_ATTRIBUTES) delete data[key];
12750
+ for (const key of URL_ATTRIBUTES) {
12751
+ const value = data[key];
12752
+ if (typeof value === "string") data[key] = stripQuery(value);
12753
+ }
12754
+ }
12755
+ /**
12756
+ * Last word on every event the public build sends: messages reduced to fixed
12757
+ * categories, no hostname, no breadcrumbs, no dependency list, no request data,
12758
+ * package-relative stack paths, and no query strings on spans.
12759
+ */
12760
+ function scrubPublicSentryEvent(event) {
12761
+ redactUnsafeSentryMessages(event);
12762
+ delete event.server_name;
12763
+ delete event.breadcrumbs;
12764
+ delete event.modules;
12765
+ delete event.request;
12766
+ for (const value of event.exception?.values ?? []) scrubStacktrace(value.stacktrace);
12767
+ for (const thread of event.threads?.values ?? []) scrubStacktrace(thread.stacktrace);
12768
+ for (const span of event.spans ?? []) scrubSpan(span);
12769
+ scrubSpan(event.contexts?.trace);
12770
+ return event;
12771
+ }
12772
+ /**
12773
+ * Privacy options shared by `initCliTelemetry` and `initPublicSentry`.
12774
+ *
12775
+ * `sendDefaultPii: false` leaves the hostname (`server_name`), console and fetch
12776
+ * breadcrumbs, absolute stack paths, the working directory's package.json
12777
+ * dependencies (`Modules`) and incoming request data in place. Console
12778
+ * breadcrumbs in the MCP include the workspace name it logs at startup.
12779
+ */
12780
+ const PUBLIC_SENTRY_PRIVACY_OPTIONS = {
12781
+ sendDefaultPii: false,
12782
+ includeServerName: false,
12783
+ maxBreadcrumbs: 0,
12784
+ integrations: (defaults) => defaults.filter((integration) => integration.name !== "Modules"),
12785
+ beforeBreadcrumb: () => null,
12786
+ beforeSend: (event) => scrubPublicSentryEvent(event),
12787
+ beforeSendTransaction: (event) => scrubPublicSentryEvent(event)
12788
+ };
12675
12789
  const DEFAULT_POSTHOG_HOST = "https://eu.i.posthog.com";
12676
12790
  /**
12677
12791
  * The shared Toggl **production** PostHog project — the same token the Track
@@ -27151,11 +27265,12 @@ async function runValidatedOperation(operation, options, deps) {
27151
27265
  }
27152
27266
  //#endregion
27153
27267
  //#region src/telemetry.ts
27154
- function envEnabled(raw) {
27155
- if (raw === void 0) return false;
27156
- const normalized = raw.trim().toLowerCase();
27157
- return normalized === "1" || normalized === "true" || normalized === "on" || normalized === "yes";
27158
- }
27268
+ /**
27269
+ * Public client DSN for the `toggl-cli` Sentry project. Safe to ship: it only
27270
+ * accepts events. The pack audit allowlists this exact value and still rejects
27271
+ * any other `ingest.sentry.io` DSN.
27272
+ */
27273
+ const PUBLIC_SENTRY_DSN = "https://499edd257e0c64778932e6b7c7a0f8e9@o43910.ingest.us.sentry.io/4512156592046080";
27159
27274
  let sentryEnabled = false;
27160
27275
  let productAnalyticsEnabled = false;
27161
27276
  let lastCommandError;
@@ -27165,23 +27280,18 @@ function initCliTelemetry(packageVersion) {
27165
27280
  packageVersion,
27166
27281
  distinctId: resolveProductAnalyticsDistinctId("cli")
27167
27282
  });
27168
- if (!envEnabled(process.env.TOGGL_SENTRY)) {
27169
- sentryEnabled = false;
27170
- return;
27171
- }
27172
- const dsn = process.env.SENTRY_DSN;
27173
- if (!dsn || dsn === "off") {
27174
- console.error("[Telemetry] TOGGL_SENTRY is set but SENTRY_DSN is missing; Sentry left disabled");
27283
+ const dsn = resolvePublicSentryDsn(PUBLIC_SENTRY_DSN);
27284
+ if (!dsn) {
27175
27285
  sentryEnabled = false;
27176
27286
  return;
27177
27287
  }
27178
27288
  Sentry.init({
27179
27289
  dsn,
27290
+ release: `@togglhq/cli@${packageVersion}`,
27180
27291
  environment: process.env.NODE_ENV ?? "production",
27181
27292
  tracesSampleRate: .1,
27182
- sendDefaultPii: false,
27183
27293
  enableLogs: false,
27184
- beforeSend: (event) => redactUnsafeSentryMessages(event)
27294
+ ...PUBLIC_SENTRY_PRIVACY_OPTIONS
27185
27295
  });
27186
27296
  Sentry.setTag("cli.session.id", crypto.randomUUID());
27187
27297
  sentryEnabled = true;
@@ -27804,4 +27914,4 @@ Common commands:
27804
27914
  //#endregion
27805
27915
  export { CommanderError as a, shutdownCliTelemetry as i, captureCliException as n, initCliTelemetry as r, createProgram as t };
27806
27916
 
27807
- //# sourceMappingURL=program-DZlKCm-L.js.map
27917
+ //# sourceMappingURL=program-CZaDiFSf.js.map
package/build/program.js CHANGED
@@ -1,2 +1,2 @@
1
- import { t as createProgram } from "./program-DZlKCm-L.js";
1
+ import { t as createProgram } from "./program-CZaDiFSf.js";
2
2
  export { createProgram };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@togglhq/cli",
3
- "version": "1.11.82",
3
+ "version": "1.11.83",
4
4
  "description": "Toggl CLI for Toggl 2.0 API operations.",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",