@debugbundle/cli 1.2.0 → 1.4.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 (2) hide show
  1. package/dist/main.cjs +541 -18
  2. package/package.json +1 -1
package/dist/main.cjs CHANGED
@@ -14771,7 +14771,7 @@ function isIpLikeHost(value) {
14771
14771
  return /^(?:\d{1,3}\.){3}\d{1,3}$/.test(host) || /^\[[0-9a-f:]+\]$/i.test(host) || host.includes(":") && /^[0-9a-f:]+$/i.test(host);
14772
14772
  }
14773
14773
 
14774
- // ../../packages/shared-types/src/capture-rules.ts
14774
+ // ../../packages/shared-types/src/capture-rule-schemas.ts
14775
14775
  var CAPTURE_RULE_EVENT_TYPES = [
14776
14776
  "backend_exception",
14777
14777
  "request_event",
@@ -14799,6 +14799,7 @@ var CaptureRuleSampleEventClassSchema = external_exports.enum(CaptureRuleSampleE
14799
14799
  var CaptureRuleRuntimeSchema = external_exports.enum(CAPTURE_RULE_RUNTIME_VALUES);
14800
14800
  var CaptureRuleEventTypeSchema = external_exports.enum(CAPTURE_RULE_EVENT_TYPES);
14801
14801
  var BrowserEventKindSchema = external_exports.enum(["window_error", "resource_error"]);
14802
+ var CaptureRuleClientKindSchema = external_exports.enum(["human", "bot", "unknown"]);
14802
14803
  function normalizeOptionalTrimmedString(value) {
14803
14804
  const trimmed = value?.trim();
14804
14805
  return trimmed && trimmed.length > 0 ? trimmed : void 0;
@@ -14876,6 +14877,9 @@ var CaptureRuleMatcherSchema = external_exports.object({
14876
14877
  message_contains: external_exports.string().min(1).max(500).optional(),
14877
14878
  message_equals: external_exports.string().min(1).max(500).optional(),
14878
14879
  browser_event_kind: BrowserEventKindSchema.optional(),
14880
+ browser_event_opaque: external_exports.boolean().optional(),
14881
+ client_kind: CaptureRuleClientKindSchema.optional(),
14882
+ bot_family: external_exports.string().min(1).max(120).optional(),
14879
14883
  resource_url: UrlMatcherSchema.optional(),
14880
14884
  request_url: UrlMatcherSchema.optional(),
14881
14885
  status_codes: external_exports.array(external_exports.number().int().min(100).max(599)).min(1).optional(),
@@ -14890,6 +14894,7 @@ var CaptureRuleMatcherSchema = external_exports.object({
14890
14894
  const errorName = normalizeOptionalTrimmedString(value.error_name);
14891
14895
  const messageContains = normalizeOptionalTrimmedString(value.message_contains);
14892
14896
  const messageEquals = normalizeOptionalTrimmedString(value.message_equals);
14897
+ const botFamily = normalizeOptionalTrimmedString(value.bot_family);
14893
14898
  const statusCodes = normalizeNumberArray(value.status_codes);
14894
14899
  if (eventTypes !== void 0) {
14895
14900
  normalized.event_types = eventTypes;
@@ -14918,6 +14923,15 @@ var CaptureRuleMatcherSchema = external_exports.object({
14918
14923
  if (value.browser_event_kind !== void 0) {
14919
14924
  normalized.browser_event_kind = value.browser_event_kind;
14920
14925
  }
14926
+ if (value.browser_event_opaque !== void 0) {
14927
+ normalized.browser_event_opaque = value.browser_event_opaque;
14928
+ }
14929
+ if (value.client_kind !== void 0) {
14930
+ normalized.client_kind = value.client_kind;
14931
+ }
14932
+ if (botFamily !== void 0) {
14933
+ normalized.bot_family = botFamily;
14934
+ }
14921
14935
  if (value.resource_url !== void 0) {
14922
14936
  normalized.resource_url = value.resource_url;
14923
14937
  }
@@ -14944,6 +14958,9 @@ var CaptureRuleMatcherSchema = external_exports.object({
14944
14958
  "message_contains",
14945
14959
  "message_equals",
14946
14960
  "browser_event_kind",
14961
+ "browser_event_opaque",
14962
+ "client_kind",
14963
+ "bot_family",
14947
14964
  "resource_url",
14948
14965
  "request_url",
14949
14966
  "status_codes",
@@ -15126,6 +15143,8 @@ var CaptureRulesFileSchema = external_exports.object({
15126
15143
  version: external_exports.literal(1),
15127
15144
  rules: external_exports.array(CaptureRuleSchema)
15128
15145
  });
15146
+
15147
+ // ../../packages/shared-types/src/capture-rule-evaluation.ts
15129
15148
  var CaptureRuleEvaluationUrlSchema = external_exports.object({
15130
15149
  host: external_exports.string().min(1).transform((value) => value.toLowerCase()).optional(),
15131
15150
  path: external_exports.string().min(1).transform((value) => value.startsWith("/") ? value : `/${value}`)
@@ -15141,11 +15160,40 @@ var CaptureRuleEvaluationContextSchema = external_exports.object({
15141
15160
  error_name: external_exports.string().min(1).optional(),
15142
15161
  message: external_exports.string().min(1).optional(),
15143
15162
  browser_event_kind: BrowserEventKindSchema.optional(),
15163
+ browser_event_opaque: external_exports.boolean().optional(),
15164
+ client_kind: CaptureRuleClientKindSchema.optional(),
15165
+ bot_family: external_exports.string().min(1).max(120).optional(),
15144
15166
  resource_url: CaptureRuleEvaluationUrlSchema.optional(),
15145
15167
  request_url: CaptureRuleEvaluationUrlSchema.optional(),
15146
15168
  status_code: external_exports.number().int().min(0).max(599).optional(),
15147
15169
  fingerprint: CaptureRuleFingerprintSchema.optional()
15148
15170
  });
15171
+ function classifyCaptureRuleClientFromUserAgent(userAgent) {
15172
+ if (userAgent === null || userAgent === void 0) {
15173
+ return { client_kind: "unknown" };
15174
+ }
15175
+ const lower = userAgent.toLowerCase();
15176
+ const knownBots = [
15177
+ { family: "Googlebot", markers: ["googlebot", "adsbot-google", "google-inspectiontool"] },
15178
+ { family: "Bingbot", markers: ["bingbot", "msnbot"] },
15179
+ { family: "DuckDuckBot", markers: ["duckduckbot"] },
15180
+ { family: "Applebot", markers: ["applebot"] },
15181
+ { family: "YandexBot", markers: ["yandexbot"] },
15182
+ { family: "Baiduspider", markers: ["baiduspider"] },
15183
+ { family: "FacebookBot", markers: ["facebookexternalhit", "facebot"] },
15184
+ { family: "LinkedInBot", markers: ["linkedinbot"] },
15185
+ { family: "TwitterBot", markers: ["twitterbot"] },
15186
+ { family: "Slackbot", markers: ["slackbot"] }
15187
+ ];
15188
+ const knownBot = knownBots.find((entry) => entry.markers.some((marker) => lower.includes(marker)));
15189
+ if (knownBot !== void 0) {
15190
+ return { client_kind: "bot", bot_family: knownBot.family };
15191
+ }
15192
+ if (["bot", "crawler", "spider", "slurp"].some((marker) => lower.includes(marker))) {
15193
+ return { client_kind: "bot", bot_family: "OtherBot" };
15194
+ }
15195
+ return { client_kind: "human" };
15196
+ }
15149
15197
 
15150
15198
  // ../../packages/shared-types/src/capture-rule-suggestions.ts
15151
15199
  var CaptureRuleSuggestionConfidenceSchema = external_exports.enum(["high", "medium", "low"]);
@@ -15355,6 +15403,12 @@ var BrowserExceptionEventSchema = external_exports.object({
15355
15403
  }).strict().optional(),
15356
15404
  opaque: external_exports.boolean()
15357
15405
  }).strict();
15406
+ var FrontendRejectionReasonSchema = external_exports.object({
15407
+ kind: external_exports.enum(["error", "string", "object", "null", "undefined", "unknown"]),
15408
+ name: external_exports.string().min(1).optional(),
15409
+ message: external_exports.string().min(1).optional(),
15410
+ preview: external_exports.string().min(1).optional()
15411
+ }).strict();
15358
15412
  var FrontendExceptionPayloadSchema = external_exports.object({
15359
15413
  name: external_exports.string().min(1),
15360
15414
  message: external_exports.string().min(1),
@@ -15367,6 +15421,7 @@ var FrontendExceptionPayloadSchema = external_exports.object({
15367
15421
  breadcrumbs: external_exports.array(FrontendExceptionBreadcrumbSchema).optional(),
15368
15422
  device: DeviceInfoSchema.nullable().optional(),
15369
15423
  browser_event: BrowserExceptionEventSchema.optional(),
15424
+ rejection_reason: FrontendRejectionReasonSchema.optional(),
15370
15425
  dom_context: external_exports.object({
15371
15426
  mode: external_exports.literal("lightweight"),
15372
15427
  html_excerpt: external_exports.string().min(1)
@@ -15818,7 +15873,7 @@ function buildSkill() {
15818
15873
  "2. Inspect the incident bundle and reproduction artifact before proposing a fix.",
15819
15874
  "3. Run `debugbundle analyze --type improvement --local` after local processing when you need a deterministic change plan.",
15820
15875
  "4. Apply the narrowest fix, then validate it with the repository test workflow from `.debugbundle/profile.json`.",
15821
- "5. When the fix is confirmed, or when the incident was intentionally generated for smoke, verification, or dogfooding, resolve it with `debugbundle resolve <incident-id>` or MCP `resolve_incident` so the open queue stays actionable.",
15876
+ "5. When the fix is confirmed, or when the incident was intentionally generated for smoke, verification, or dogfooding, resolve it with `debugbundle resolve <incident-id> [incident-id ...]` or MCP `resolve_incident` / `resolve_incidents` so the open queue stays actionable.",
15822
15877
  "",
15823
15878
  "## Incident Hygiene",
15824
15879
  "",
@@ -15827,6 +15882,15 @@ function buildSkill() {
15827
15882
  "- Reopen or leave open if the failure is still present, the validation is incomplete, or the incident represents a live unresolved problem.",
15828
15883
  "- If a resolved incident regresses, let the platform move it back to `regressed` through normal incident lifecycle behavior.",
15829
15884
  "",
15885
+ "## Noise Management",
15886
+ "",
15887
+ "When incident evidence shows repeated low-value operational noise rather than a product bug, evaluate whether a scoped capture rule or capture-policy path rule should handle future matches.",
15888
+ "",
15889
+ "- Run `debugbundle capture-rule suggest <incident-id> --json` before creating a manual rule. Apply deterministic suggestions with `debugbundle capture-rule create-from-suggestion <incident-id> --suggestion-id <id>` after confirming the scope is safe.",
15890
+ "- Prefer project capture rules for operational noise because they are centralized, auditable, and enforced by ingestion and processing. Use SDK `beforeSend` only for app-owned local policy such as final redaction or events that must never leave the runtime.",
15891
+ "- Scope frontend noise by structured evidence such as service, environment, `browser_event_kind`, `browser_event_opaque`, `client_kind`, `bot_family`, and message fields. Do not broadly demote generic `Unhandled promise rejection` incidents without bot-scoped or otherwise narrow evidence.",
15892
+ "- For expected or intentionally promoted 4xx responses on known routes, use capture-policy client-error path rules instead of promoting all client errors: `debugbundle capture-policy set --client-error-path-rule <status=/path/*@GET>`.",
15893
+ "",
15830
15894
  "## Profile Validation",
15831
15895
  "",
15832
15896
  "Use this task after setup or whenever architecture changes make the static profile stale.",
@@ -15889,10 +15953,21 @@ function buildCliReference() {
15889
15953
  "- `debugbundle explain <incident-id> [--source <local|cloud>] [--json]`",
15890
15954
  "- `debugbundle bundle <incident-id> [--source <local|cloud>] [--json]`",
15891
15955
  "- `debugbundle reproduce <incident-id> [--source <local|cloud>] [--json]`",
15892
- "- `debugbundle resolve <incident-id> [--source <local|cloud>] [--json]`",
15893
- "- `debugbundle reopen <incident-id> [--source <local|cloud>] [--json]`",
15956
+ "- `debugbundle resolve <incident-id> [incident-id ...] [--source <local|cloud>] [--json]`",
15957
+ "- `debugbundle reopen <incident-id> [incident-id ...] [--source <local|cloud>] [--json]`",
15894
15958
  "- `debugbundle analyze --type improvement --local`",
15895
15959
  "",
15960
+ "## Noise Management",
15961
+ "",
15962
+ "- `debugbundle capture-rule suggest <incident-id> [--auth-file <path>] [--json]`",
15963
+ "- `debugbundle capture-rule create-from-suggestion <incident-id> --suggestion-id <id> [--name <name>] [--expires-at <ISO8601>] [--auth-file <path>] [--json]`",
15964
+ "- `debugbundle capture-rule list --project-id <id> [--auth-file <path>] [--json]`",
15965
+ "- `debugbundle capture-rule create --project-id <id> --name <name> --action <demote|sample|drop> --matcher-json <json> [--auth-file <path>] [--json]`",
15966
+ "- `debugbundle capture-policy get [--project <id>] [--json]`",
15967
+ "- `debugbundle capture-policy set [--project <id>] --client-error-path-rule <404=/path/*@GET,POST> [--json]`",
15968
+ "",
15969
+ "Use capture-rule suggestions for repeated operational noise after inspecting an incident bundle. Use capture-policy client-error path rules for route-scoped 4xx incidents instead of promoting all client errors.",
15970
+ "",
15896
15971
  "## Operational Paths",
15897
15972
  "",
15898
15973
  "- `.debugbundle/profile.json` \u2014 committed project map and agent validation state",
@@ -15923,7 +15998,7 @@ function buildCliReference() {
15923
15998
  "```bash",
15924
15999
  "debugbundle incidents --status open --json \\",
15925
16000
  ` | jq -r '.incidents[] | select(.title | test("smoke test|dogfood|verification|synthetic"; "i")) | .incident_id' \\`,
15926
- " | xargs -n1 debugbundle resolve",
16001
+ " | xargs debugbundle resolve",
15927
16002
  "```",
15928
16003
  ""
15929
16004
  ].join("\n");
@@ -15942,19 +16017,28 @@ function buildMcpReference() {
15942
16017
  "- `get_incident_context` \u2014 fetch deterministic explanation context for triage.",
15943
16018
  "- `get_bundle` \u2014 fetch the full debug bundle before proposing a fix.",
15944
16019
  "- `get_reproduction` \u2014 fetch reproduction guidance before editing code.",
15945
- "- `resolve_incident` / `reopen_incident` \u2014 update lifecycle state after validation.",
16020
+ "- `resolve_incident` / `resolve_incidents` / `reopen_incident` / `reopen_incidents` \u2014 update lifecycle state after validation.",
15946
16021
  "- `analyze` \u2014 run local agent-oriented analysis from local bundles and skill schemas.",
15947
16022
  "",
15948
16023
  "- Prefer bundle retrieval tools before reading raw repository files.",
15949
16024
  "- Use MCP bundle access when the current issue originated in production.",
15950
- "- Resolve fixed or intentionally generated incidents with `resolve_incident` so open incidents stay actionable.",
16025
+ "- Resolve fixed or intentionally generated incidents with `resolve_incident` or `resolve_incidents` so open incidents stay actionable.",
15951
16026
  "- Fall back to local CLI processing when the project is local-only.",
15952
16027
  "",
16028
+ "## Noise and Capture Policy Tools",
16029
+ "",
16030
+ "- `suggest_capture_rules_from_incident` \u2014 generate deterministic capture-rule suggestions from an incident bundle.",
16031
+ "- `create_capture_rule_from_incident_suggestion` \u2014 apply a confirmed suggestion.",
16032
+ "- `list_capture_rules`, `create_capture_rule`, `update_capture_rule`, `delete_capture_rule` \u2014 manage project capture rules.",
16033
+ "- `get_capture_policy`, `update_capture_policy` \u2014 review or update capture policy, including path-scoped client-error incident rules.",
16034
+ "",
16035
+ "Use these tools for repeated low-value operational noise only after inspecting incident evidence. Keep frontend suppression scoped by structured browser and client signals, and use path-scoped capture policy for known 4xx routes.",
16036
+ "",
15953
16037
  "## Smoke-Test Cleanup Recipe",
15954
16038
  "",
15955
16039
  '1. Call `list_incidents` with `status: "open"`.',
15956
16040
  "2. Filter incidents whose titles show they were intentionally generated for smoke, dogfood, verification, or synthetic checks.",
15957
- "3. Call `resolve_incident` for each verified synthetic incident.",
16041
+ "3. Call `resolve_incidents` for verified synthetic incidents, or `resolve_incident` for a single incident.",
15958
16042
  "4. Call `list_incidents` again and confirm the open queue only contains actionable failures.",
15959
16043
  ""
15960
16044
  ].join("\n");
@@ -16074,6 +16158,16 @@ function buildSkillEvals() {
16074
16158
  "Leave unresolved incidents open when the failure is still live or unverified."
16075
16159
  ]
16076
16160
  },
16161
+ {
16162
+ name: "noise_management_guidance",
16163
+ prompt: "The same low-value frontend incident keeps reopening. Confirm the skill tells the agent how to evaluate operational noise without hiding real bugs.",
16164
+ expected_behavior: [
16165
+ "Inspect incident evidence before creating a rule.",
16166
+ "Use capture-rule suggestions for repeated operational noise.",
16167
+ "Keep generic frontend suppression narrow with structured browser or bot signals.",
16168
+ "Use capture-policy path rules for known route-scoped 4xx incidents."
16169
+ ]
16170
+ },
16077
16171
  {
16078
16172
  name: "artifact_path_discovery",
16079
16173
  prompt: "The user reports an unknown local runtime error. Confirm the skill tells the agent which DebugBundle paths and commands to inspect first.",
@@ -16736,6 +16830,9 @@ function createRetrievalApi(client) {
16736
16830
  if (input2.severity !== void 0) {
16737
16831
  query.set("severity", input2.severity);
16738
16832
  }
16833
+ if (input2.firstSeenAfter !== void 0) {
16834
+ query.set("first_seen_after", input2.firstSeenAfter);
16835
+ }
16739
16836
  if (input2.cursor !== void 0) {
16740
16837
  query.set("cursor", input2.cursor);
16741
16838
  }
@@ -16992,6 +17089,88 @@ function createRetrievalApi(client) {
16992
17089
  };
16993
17090
  }
16994
17091
 
17092
+ // ../../packages/storage/src/account-analytics-store.ts
17093
+ var ACCOUNT_METRIC_KEYS = [
17094
+ "account_created",
17095
+ "account_deleted",
17096
+ "project_created",
17097
+ "project_deleted",
17098
+ "raw_events_accepted",
17099
+ "raw_events_rejected",
17100
+ "events_rejected_malformed",
17101
+ "events_rejected_rate_limited",
17102
+ "events_rejected_quota",
17103
+ "events_rejected_capture_policy",
17104
+ "events_rejected_capture_rule",
17105
+ "billable_events_counted",
17106
+ "incident_signal_events_counted",
17107
+ "context_signal_events_counted",
17108
+ "operational_signal_events_counted",
17109
+ "local_verification_events_accepted",
17110
+ "cloud_verification_events_accepted",
17111
+ "incidents_opened",
17112
+ "incidents_resolved",
17113
+ "incidents_reopened",
17114
+ "incidents_regressed",
17115
+ "incident_occurrences",
17116
+ "incident_occurrences_high_severity",
17117
+ "incident_occurrences_critical_severity",
17118
+ "incidents_auto_detected_spiking",
17119
+ "failure_bundles_created",
17120
+ "failure_bundles_updated",
17121
+ "failure_bundle_generations_failed",
17122
+ "improvement_bundles_created",
17123
+ "improvement_bundles_updated",
17124
+ "improvement_bundle_generations_failed",
17125
+ "reproductions_created",
17126
+ "reproductions_failed",
17127
+ "retention_bundle_owners_rotated",
17128
+ "improvements_opened",
17129
+ "improvements_resolved",
17130
+ "improvements_reopened",
17131
+ "improvements_snoozed",
17132
+ "recurring_incident_improvements_opened",
17133
+ "post_deploy_regression_improvements_opened",
17134
+ "slow_request_improvements_opened",
17135
+ "request_failure_improvements_opened",
17136
+ "warning_log_improvements_opened",
17137
+ "alert_deliveries_created",
17138
+ "alert_deliveries_delivered",
17139
+ "alert_deliveries_failed",
17140
+ "alert_email_digests_sent",
17141
+ "operational_emails_sent",
17142
+ "weekly_reports_sent",
17143
+ "weekly_reports_failed",
17144
+ "webhook_deliveries_created",
17145
+ "webhook_deliveries_delivered",
17146
+ "webhook_deliveries_failed",
17147
+ "webhooks_auto_disabled",
17148
+ "github_dispatches_created",
17149
+ "github_dispatches_delivered",
17150
+ "github_dispatches_failed",
17151
+ "github_dispatch_rules_created",
17152
+ "github_dispatch_rules_deleted",
17153
+ "remote_probe_activations_created",
17154
+ "remote_probe_activations_expired",
17155
+ "probe_events_accepted",
17156
+ "capture_rules_created",
17157
+ "capture_rules_deleted",
17158
+ "capture_policy_updates",
17159
+ "trial_started",
17160
+ "trial_converted",
17161
+ "trial_expired",
17162
+ "plan_upgraded",
17163
+ "plan_downgraded",
17164
+ "capacity_units_purchased",
17165
+ "capacity_units_reduced",
17166
+ "allowance_warning_emails_sent",
17167
+ "allowance_limit_emails_sent",
17168
+ "projects_existing_at_account_deletion",
17169
+ "open_incidents_existing_at_account_deletion",
17170
+ "open_improvements_existing_at_account_deletion"
17171
+ ];
17172
+ var AccountMetricKeySchema = external_exports.enum(ACCOUNT_METRIC_KEYS);
17173
+
16995
17174
  // ../../packages/storage/src/incident-context.ts
16996
17175
  function isRecord2(value) {
16997
17176
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -17088,6 +17267,33 @@ function buildRedactionRecord(bundleBody) {
17088
17267
  notes: readString(redaction["notes"])
17089
17268
  };
17090
17269
  }
17270
+ function buildBrowserSignalRecord(bundleBody) {
17271
+ const bundle = isRecord2(bundleBody) ? bundleBody : {};
17272
+ const context = isRecord2(bundle["context"]) ? bundle["context"] : {};
17273
+ const frontend = isRecord2(context["frontend"]) ? context["frontend"] : {};
17274
+ const exceptions = Array.isArray(frontend["exceptions"]) ? frontend["exceptions"] : [];
17275
+ let exception;
17276
+ for (let index = exceptions.length - 1; index >= 0; index -= 1) {
17277
+ const candidate = exceptions[index];
17278
+ if (isRecord2(candidate) && isRecord2(candidate["browser_event"])) {
17279
+ exception = candidate;
17280
+ break;
17281
+ }
17282
+ }
17283
+ const browserEvent = isRecord2(exception) && isRecord2(exception["browser_event"]) ? exception["browser_event"] : null;
17284
+ const device = isRecord2(context["device"]) ? context["device"] : {};
17285
+ const client = classifyCaptureRuleClientFromUserAgent(readString(device["user_agent"]) ?? void 0);
17286
+ if (browserEvent === null && client.client_kind === "unknown") {
17287
+ return null;
17288
+ }
17289
+ return {
17290
+ browser_event_kind: readString(browserEvent?.["kind"]),
17291
+ browser_event_opaque: readBoolean(browserEvent?.["opaque"]),
17292
+ browser_event_message: readString(browserEvent?.["message"]),
17293
+ client_kind: client.client_kind,
17294
+ bot_family: client.bot_family ?? null
17295
+ };
17296
+ }
17091
17297
  function buildVisibilityRecord(input2) {
17092
17298
  const routeTarget = input2.primarySignal.route_template ?? input2.primarySignal.request_path;
17093
17299
  const matchedFields = input2.incident.matched_fields.length === 0 ? "none" : input2.incident.matched_fields.join(", ");
@@ -17125,6 +17331,14 @@ function buildSuggestedNextChecks(input2) {
17125
17331
  if (input2.deploy.regression_window === true || input2.incident.status === "regressed") {
17126
17332
  suggestions.push("Compare this incident against the most recent deploy and recent regressions.");
17127
17333
  }
17334
+ if (input2.browserSignal?.browser_event_opaque === true) {
17335
+ suggestions.push("Treat the browser event as opaque; inspect CSP, cross-origin scripts, resource loading, and framework error boundaries before changing application code.");
17336
+ }
17337
+ if (input2.browserSignal?.client_kind === "bot") {
17338
+ suggestions.push(
17339
+ `Review whether ${input2.browserSignal.bot_family ?? "bot"} traffic is operational noise before applying a bot-scoped capture rule.`
17340
+ );
17341
+ }
17128
17342
  if (input2.reproduction.status === "pending") {
17129
17343
  suggestions.push("Recheck reproduction guidance after the reproduction artifact is ready.");
17130
17344
  }
@@ -17145,6 +17359,7 @@ function buildIncidentContextRecord(input2) {
17145
17359
  primarySignal
17146
17360
  });
17147
17361
  const redaction = buildRedactionRecord(bundleBody);
17362
+ const browserSignal = buildBrowserSignalRecord(bundleBody);
17148
17363
  return {
17149
17364
  incident: input2.incident,
17150
17365
  incident_reason: incidentReason,
@@ -17160,13 +17375,15 @@ function buildIncidentContextRecord(input2) {
17160
17375
  },
17161
17376
  visibility,
17162
17377
  redaction,
17378
+ browser_signal: browserSignal,
17163
17379
  suggested_next_checks: buildSuggestedNextChecks({
17164
17380
  incident: input2.incident,
17165
17381
  bundle: input2.bundle,
17166
17382
  reproduction: input2.reproduction,
17167
17383
  logs,
17168
17384
  primarySignal,
17169
- deploy
17385
+ deploy,
17386
+ browserSignal
17170
17387
  })
17171
17388
  };
17172
17389
  }
@@ -17239,6 +17456,9 @@ function deriveIncidentReasonFromSourceEventTypes(eventTypes) {
17239
17456
  // ../../packages/auth/src/primitives.ts
17240
17457
  var import_argon2 = require("@node-rs/argon2");
17241
17458
 
17459
+ // ../../packages/auth/src/account-deletion-auth.ts
17460
+ var DEFAULT_ACCOUNT_DELETION_CODE_LIFETIME_MS = 1e3 * 60 * 10;
17461
+
17242
17462
  // ../../packages/auth/src/web-session-auth.ts
17243
17463
  var DEFAULT_SESSION_LIFETIME_MS = 1e3 * 60 * 60 * 24 * 7;
17244
17464
  var DEFAULT_EMAIL_AUTH_CODE_LIFETIME_MS = 1e3 * 60 * 10;
@@ -17950,6 +18170,26 @@ var STORAGE_BOOTSTRAP_STATEMENTS = [
17950
18170
  CREATE INDEX email_auth_challenges_code_hash_idx
17951
18171
  ON email_auth_challenges (code_hash)
17952
18172
  `,
18173
+ `
18174
+ CREATE TABLE account_deletion_challenges (
18175
+ id uuid PRIMARY KEY,
18176
+ organization_id uuid NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
18177
+ user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE,
18178
+ email text NOT NULL,
18179
+ code_hash text NOT NULL,
18180
+ created_at timestamptz NOT NULL DEFAULT now(),
18181
+ expires_at timestamptz NOT NULL,
18182
+ used_at timestamptz
18183
+ )
18184
+ `,
18185
+ `
18186
+ CREATE INDEX account_deletion_challenges_scope_idx
18187
+ ON account_deletion_challenges (organization_id, user_id, lower(email), created_at DESC)
18188
+ `,
18189
+ `
18190
+ CREATE INDEX account_deletion_challenges_code_hash_idx
18191
+ ON account_deletion_challenges (code_hash)
18192
+ `,
17953
18193
  `
17954
18194
  CREATE TABLE github_device_authorizations (
17955
18195
  id uuid PRIMARY KEY,
@@ -18660,6 +18900,103 @@ var STORAGE_BOOTSTRAP_STATEMENTS = [
18660
18900
  PRIMARY KEY (organization_id, period_starts_at)
18661
18901
  )
18662
18902
  `,
18903
+ `
18904
+ CREATE TABLE project_usage_counters (
18905
+ project_id uuid NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
18906
+ period_starts_at timestamptz NOT NULL,
18907
+ raw_ingested_events integer NOT NULL DEFAULT 0,
18908
+ updated_at timestamptz NOT NULL DEFAULT now(),
18909
+ PRIMARY KEY (project_id, period_starts_at)
18910
+ )
18911
+ `,
18912
+ `
18913
+ CREATE TABLE account_analytics_accounts (
18914
+ analytics_account_id uuid PRIMARY KEY,
18915
+ organization_id uuid UNIQUE,
18916
+ organization_id_hash text NOT NULL UNIQUE,
18917
+ created_at timestamptz NOT NULL,
18918
+ first_seen_at timestamptz NOT NULL,
18919
+ metrics_collection_started_at timestamptz NOT NULL,
18920
+ backfilled_from_retained_rows_at timestamptz,
18921
+ deleted_at timestamptz,
18922
+ initial_plan text,
18923
+ latest_known_plan text,
18924
+ latest_capacity_units integer,
18925
+ account_deleted boolean NOT NULL DEFAULT false,
18926
+ metrics_schema_version integer NOT NULL DEFAULT 1,
18927
+ updated_at timestamptz NOT NULL DEFAULT now()
18928
+ )
18929
+ `,
18930
+ `
18931
+ CREATE TABLE account_metric_periods (
18932
+ analytics_account_id uuid NOT NULL REFERENCES account_analytics_accounts(analytics_account_id),
18933
+ period_grain text NOT NULL CHECK (period_grain IN ('day', 'month', 'year', 'lifetime')),
18934
+ period_starts_at timestamptz NOT NULL,
18935
+ metric_key text NOT NULL,
18936
+ metric_value bigint NOT NULL DEFAULT 0,
18937
+ updated_at timestamptz NOT NULL DEFAULT now(),
18938
+ PRIMARY KEY (analytics_account_id, period_grain, period_starts_at, metric_key)
18939
+ )
18940
+ `,
18941
+ `
18942
+ CREATE INDEX account_metric_periods_grain_period_metric_idx
18943
+ ON account_metric_periods (period_grain, period_starts_at, metric_key)
18944
+ `,
18945
+ `
18946
+ CREATE INDEX account_metric_periods_account_grain_period_idx
18947
+ ON account_metric_periods (analytics_account_id, period_grain, period_starts_at)
18948
+ `,
18949
+ `
18950
+ CREATE TABLE account_metric_events (
18951
+ dedupe_key_hash text PRIMARY KEY,
18952
+ analytics_account_id uuid NOT NULL REFERENCES account_analytics_accounts(analytics_account_id),
18953
+ metric_source text NOT NULL,
18954
+ occurred_at timestamptz NOT NULL,
18955
+ recorded_at timestamptz NOT NULL DEFAULT now(),
18956
+ metric_deltas jsonb NOT NULL
18957
+ )
18958
+ `,
18959
+ `
18960
+ CREATE TABLE account_payment_retention_records (
18961
+ id uuid PRIMARY KEY,
18962
+ analytics_account_id uuid NOT NULL REFERENCES account_analytics_accounts(analytics_account_id),
18963
+ organization_id_hash text NOT NULL,
18964
+ provider text NOT NULL,
18965
+ plan text,
18966
+ billing_state text,
18967
+ stripe_customer_id text,
18968
+ stripe_subscription_id text,
18969
+ billing_period_starts_at timestamptz,
18970
+ billing_period_ends_at timestamptz,
18971
+ additional_capacity_units integer,
18972
+ last_billing_event_id text,
18973
+ account_deleted_at timestamptz NOT NULL,
18974
+ recorded_at timestamptz NOT NULL DEFAULT now(),
18975
+ updated_at timestamptz NOT NULL DEFAULT now(),
18976
+ UNIQUE (analytics_account_id, provider)
18977
+ )
18978
+ `,
18979
+ `
18980
+ CREATE INDEX account_payment_retention_records_provider_idx
18981
+ ON account_payment_retention_records (provider, account_deleted_at DESC)
18982
+ `,
18983
+ `
18984
+ CREATE TABLE account_payment_provider_events (
18985
+ provider_event_key text PRIMARY KEY,
18986
+ analytics_account_id uuid NOT NULL REFERENCES account_analytics_accounts(analytics_account_id),
18987
+ organization_id_hash text NOT NULL,
18988
+ provider text NOT NULL,
18989
+ provider_event_id text NOT NULL,
18990
+ provider_event_type text NOT NULL,
18991
+ processed_at timestamptz NOT NULL,
18992
+ account_deleted_at timestamptz NOT NULL,
18993
+ recorded_at timestamptz NOT NULL DEFAULT now()
18994
+ )
18995
+ `,
18996
+ `
18997
+ CREATE UNIQUE INDEX account_payment_provider_events_provider_event_key
18998
+ ON account_payment_provider_events (provider, provider_event_id)
18999
+ `,
18663
19000
  `
18664
19001
  CREATE TABLE operational_email_deliveries (
18665
19002
  id uuid PRIMARY KEY,
@@ -19428,6 +19765,141 @@ var STORAGE_SCHEMA_MIGRATIONS = [
19428
19765
  statements: [
19429
19766
  "ALTER TABLE capture_policies ADD COLUMN IF NOT EXISTS immediate_client_error_path_rules jsonb"
19430
19767
  ]
19768
+ }),
19769
+ defineStorageSchemaMigration({
19770
+ id: "202606100001_add_project_usage_counters",
19771
+ description: "Add durable project-level raw ingestion counters for project dashboard metrics.",
19772
+ statements: [
19773
+ `
19774
+ CREATE TABLE IF NOT EXISTS project_usage_counters (
19775
+ project_id uuid NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
19776
+ period_starts_at timestamptz NOT NULL,
19777
+ raw_ingested_events integer NOT NULL DEFAULT 0,
19778
+ updated_at timestamptz NOT NULL DEFAULT now(),
19779
+ PRIMARY KEY (project_id, period_starts_at)
19780
+ )
19781
+ `
19782
+ ]
19783
+ }),
19784
+ defineStorageSchemaMigration({
19785
+ id: "202606100002_add_account_deletion_challenges",
19786
+ description: "Add scoped OTP challenges for account deletion confirmation.",
19787
+ statements: [
19788
+ `
19789
+ CREATE TABLE IF NOT EXISTS account_deletion_challenges (
19790
+ id uuid PRIMARY KEY,
19791
+ organization_id uuid NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
19792
+ user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE,
19793
+ email text NOT NULL,
19794
+ code_hash text NOT NULL,
19795
+ created_at timestamptz NOT NULL DEFAULT now(),
19796
+ expires_at timestamptz NOT NULL,
19797
+ used_at timestamptz
19798
+ )
19799
+ `,
19800
+ `
19801
+ CREATE INDEX IF NOT EXISTS account_deletion_challenges_scope_idx
19802
+ ON account_deletion_challenges (organization_id, user_id, lower(email), created_at DESC)
19803
+ `,
19804
+ `
19805
+ CREATE INDEX IF NOT EXISTS account_deletion_challenges_code_hash_idx
19806
+ ON account_deletion_challenges (code_hash)
19807
+ `
19808
+ ]
19809
+ }),
19810
+ defineStorageSchemaMigration({
19811
+ id: "202606100003_add_account_analytics_and_payment_retention",
19812
+ description: "Add deletion-safe account analytics and payment retention ledgers.",
19813
+ statements: [
19814
+ `
19815
+ CREATE TABLE IF NOT EXISTS account_analytics_accounts (
19816
+ analytics_account_id uuid PRIMARY KEY,
19817
+ organization_id uuid UNIQUE,
19818
+ organization_id_hash text NOT NULL UNIQUE,
19819
+ created_at timestamptz NOT NULL,
19820
+ first_seen_at timestamptz NOT NULL,
19821
+ metrics_collection_started_at timestamptz NOT NULL,
19822
+ backfilled_from_retained_rows_at timestamptz,
19823
+ deleted_at timestamptz,
19824
+ initial_plan text,
19825
+ latest_known_plan text,
19826
+ latest_capacity_units integer,
19827
+ account_deleted boolean NOT NULL DEFAULT false,
19828
+ metrics_schema_version integer NOT NULL DEFAULT 1,
19829
+ updated_at timestamptz NOT NULL DEFAULT now()
19830
+ )
19831
+ `,
19832
+ `
19833
+ CREATE TABLE IF NOT EXISTS account_metric_periods (
19834
+ analytics_account_id uuid NOT NULL REFERENCES account_analytics_accounts(analytics_account_id),
19835
+ period_grain text NOT NULL CHECK (period_grain IN ('day', 'month', 'year', 'lifetime')),
19836
+ period_starts_at timestamptz NOT NULL,
19837
+ metric_key text NOT NULL,
19838
+ metric_value bigint NOT NULL DEFAULT 0,
19839
+ updated_at timestamptz NOT NULL DEFAULT now(),
19840
+ PRIMARY KEY (analytics_account_id, period_grain, period_starts_at, metric_key)
19841
+ )
19842
+ `,
19843
+ `
19844
+ CREATE INDEX IF NOT EXISTS account_metric_periods_grain_period_metric_idx
19845
+ ON account_metric_periods (period_grain, period_starts_at, metric_key)
19846
+ `,
19847
+ `
19848
+ CREATE INDEX IF NOT EXISTS account_metric_periods_account_grain_period_idx
19849
+ ON account_metric_periods (analytics_account_id, period_grain, period_starts_at)
19850
+ `,
19851
+ `
19852
+ CREATE TABLE IF NOT EXISTS account_metric_events (
19853
+ dedupe_key_hash text PRIMARY KEY,
19854
+ analytics_account_id uuid NOT NULL REFERENCES account_analytics_accounts(analytics_account_id),
19855
+ metric_source text NOT NULL,
19856
+ occurred_at timestamptz NOT NULL,
19857
+ recorded_at timestamptz NOT NULL DEFAULT now(),
19858
+ metric_deltas jsonb NOT NULL
19859
+ )
19860
+ `,
19861
+ `
19862
+ CREATE TABLE IF NOT EXISTS account_payment_retention_records (
19863
+ id uuid PRIMARY KEY,
19864
+ analytics_account_id uuid NOT NULL REFERENCES account_analytics_accounts(analytics_account_id),
19865
+ organization_id_hash text NOT NULL,
19866
+ provider text NOT NULL,
19867
+ plan text,
19868
+ billing_state text,
19869
+ stripe_customer_id text,
19870
+ stripe_subscription_id text,
19871
+ billing_period_starts_at timestamptz,
19872
+ billing_period_ends_at timestamptz,
19873
+ additional_capacity_units integer,
19874
+ last_billing_event_id text,
19875
+ account_deleted_at timestamptz NOT NULL,
19876
+ recorded_at timestamptz NOT NULL DEFAULT now(),
19877
+ updated_at timestamptz NOT NULL DEFAULT now(),
19878
+ UNIQUE (analytics_account_id, provider)
19879
+ )
19880
+ `,
19881
+ `
19882
+ CREATE INDEX IF NOT EXISTS account_payment_retention_records_provider_idx
19883
+ ON account_payment_retention_records (provider, account_deleted_at DESC)
19884
+ `,
19885
+ `
19886
+ CREATE TABLE IF NOT EXISTS account_payment_provider_events (
19887
+ provider_event_key text PRIMARY KEY,
19888
+ analytics_account_id uuid NOT NULL REFERENCES account_analytics_accounts(analytics_account_id),
19889
+ organization_id_hash text NOT NULL,
19890
+ provider text NOT NULL,
19891
+ provider_event_id text NOT NULL,
19892
+ provider_event_type text NOT NULL,
19893
+ processed_at timestamptz NOT NULL,
19894
+ account_deleted_at timestamptz NOT NULL,
19895
+ recorded_at timestamptz NOT NULL DEFAULT now()
19896
+ )
19897
+ `,
19898
+ `
19899
+ CREATE UNIQUE INDEX IF NOT EXISTS account_payment_provider_events_provider_event_key
19900
+ ON account_payment_provider_events (provider, provider_event_id)
19901
+ `
19902
+ ]
19431
19903
  })
19432
19904
  ];
19433
19905
 
@@ -19631,7 +20103,7 @@ async function listLocalIncidents(input2, dependencies) {
19631
20103
  return true;
19632
20104
  }
19633
20105
  return incident.status === input2.status;
19634
- }).filter((incident) => input2.severity === void 0 ? true : incident.severity === input2.severity).sort(sortIncidentsDescending);
20106
+ }).filter((incident) => input2.severity === void 0 ? true : incident.severity === input2.severity).filter((incident) => input2.firstSeenAfter === void 0 ? true : incident.first_seen_at >= input2.firstSeenAfter).sort(sortIncidentsDescending);
19635
20107
  const startIndex = input2.cursor === void 0 ? 0 : incidents.findIndex((incident) => buildCursor(incident) === input2.cursor) + 1;
19636
20108
  const pagedIncidents = input2.limit === void 0 ? incidents.slice(startIndex) : incidents.slice(startIndex, startIndex + input2.limit);
19637
20109
  const hasMore = input2.limit !== void 0 && startIndex + input2.limit < incidents.length;
@@ -20175,6 +20647,10 @@ var import_node_path9 = require("node:path");
20175
20647
 
20176
20648
  // ../../packages/project-management-client/src/index.ts
20177
20649
  var ProjectMetricsSchema = external_exports.object({
20650
+ open_incidents: external_exports.number().int().nonnegative().default(0),
20651
+ regressed_incidents: external_exports.number().int().nonnegative().default(0),
20652
+ opened_incidents_today: external_exports.number().int().nonnegative().default(0),
20653
+ opened_incidents_month: external_exports.number().int().nonnegative().default(0),
20178
20654
  monthly_bundle_requests: external_exports.number().int().nonnegative(),
20179
20655
  monthly_raw_ingested_events: external_exports.number().int().nonnegative(),
20180
20656
  retained_bundles: external_exports.number().int().nonnegative(),
@@ -27189,6 +27665,7 @@ async function listAllCloudIncidents(input2, api) {
27189
27665
  ...input2.service === void 0 ? {} : { service: input2.service },
27190
27666
  ...input2.status === void 0 ? {} : { status: input2.status },
27191
27667
  ...input2.severity === void 0 ? {} : { severity: input2.severity },
27668
+ ...input2.firstSeenAfter === void 0 ? {} : { firstSeenAfter: input2.firstSeenAfter },
27192
27669
  ...cursor === void 0 ? {} : { cursor }
27193
27670
  });
27194
27671
  incidents.push(
@@ -27209,7 +27686,8 @@ async function mapCombinedIncidentListResult(input2, api, dependencies) {
27209
27686
  ...input2.environment === void 0 ? {} : { environment: input2.environment },
27210
27687
  ...input2.service === void 0 ? {} : { service: input2.service },
27211
27688
  ...input2.status === void 0 ? {} : { status: input2.status },
27212
- ...input2.severity === void 0 ? {} : { severity: input2.severity }
27689
+ ...input2.severity === void 0 ? {} : { severity: input2.severity },
27690
+ ...input2.firstSeenAfter === void 0 ? {} : { firstSeenAfter: input2.firstSeenAfter }
27213
27691
  },
27214
27692
  dependencies
27215
27693
  );
@@ -27220,7 +27698,8 @@ async function mapCombinedIncidentListResult(input2, api, dependencies) {
27220
27698
  ...input2.environment === void 0 ? {} : { environment: input2.environment },
27221
27699
  ...input2.service === void 0 ? {} : { service: input2.service },
27222
27700
  ...input2.status === void 0 ? {} : { status: input2.status },
27223
- ...input2.severity === void 0 ? {} : { severity: input2.severity }
27701
+ ...input2.severity === void 0 ? {} : { severity: input2.severity },
27702
+ ...input2.firstSeenAfter === void 0 ? {} : { firstSeenAfter: input2.firstSeenAfter }
27224
27703
  },
27225
27704
  api
27226
27705
  );
@@ -27259,6 +27738,9 @@ async function listIncidentsCommand(input2, api) {
27259
27738
  if (input2.severity !== void 0) {
27260
27739
  requestInput.severity = input2.severity;
27261
27740
  }
27741
+ if (input2.firstSeenAfter !== void 0) {
27742
+ requestInput.firstSeenAfter = input2.firstSeenAfter;
27743
+ }
27262
27744
  if (input2.cursor !== void 0) {
27263
27745
  requestInput.cursor = input2.cursor;
27264
27746
  }
@@ -27290,6 +27772,7 @@ async function listIncidentsWithAuthCommand(input2, dependencies) {
27290
27772
  ...input2.service === void 0 ? {} : { service: input2.service },
27291
27773
  ...input2.status === void 0 ? {} : { status: input2.status },
27292
27774
  ...input2.severity === void 0 ? {} : { severity: input2.severity },
27775
+ ...input2.firstSeenAfter === void 0 ? {} : { firstSeenAfter: input2.firstSeenAfter },
27293
27776
  ...input2.cursor === void 0 ? {} : { cursor: input2.cursor },
27294
27777
  ...input2.limit === void 0 ? {} : { limit: input2.limit }
27295
27778
  },
@@ -27314,6 +27797,7 @@ async function listIncidentsWithAuthCommand(input2, dependencies) {
27314
27797
  ...input2.service === void 0 ? {} : { service: input2.service },
27315
27798
  ...input2.status === void 0 ? {} : { status: input2.status },
27316
27799
  ...input2.severity === void 0 ? {} : { severity: input2.severity },
27800
+ ...input2.firstSeenAfter === void 0 ? {} : { firstSeenAfter: input2.firstSeenAfter },
27317
27801
  ...input2.cursor === void 0 ? {} : { cursor: input2.cursor },
27318
27802
  ...input2.limit === void 0 ? {} : { limit: input2.limit },
27319
27803
  ...input2.json === void 0 ? {} : { json: input2.json }
@@ -27349,6 +27833,9 @@ async function listIncidentsWithAuthCommand(input2, dependencies) {
27349
27833
  if (input2.severity !== void 0) {
27350
27834
  commandInput.severity = input2.severity;
27351
27835
  }
27836
+ if (input2.firstSeenAfter !== void 0) {
27837
+ commandInput.firstSeenAfter = input2.firstSeenAfter;
27838
+ }
27352
27839
  if (input2.cursor !== void 0) {
27353
27840
  commandInput.cursor = input2.cursor;
27354
27841
  }
@@ -29073,7 +29560,7 @@ var CLI_USAGE_LINES = [
29073
29560
  " debugbundle login --github-device [--label <label>] [--base-url <url>] [--auth-file <path>] [--json]",
29074
29561
  " debugbundle profile validate [--json]",
29075
29562
  " debugbundle whoami [--auth-file <path>] [--json]",
29076
- " debugbundle incidents [--source <local|cloud>] [--project-id <id>] [--environment <name>] [--service <name>] [--status <status>] [--severity <severity>] [--cursor <cursor>] [--limit <n>] [--auth-file <path>] [--json]",
29563
+ " debugbundle incidents [--source <local|cloud>] [--project-id <id>] [--environment <name>] [--service <name>] [--status <status>] [--severity <severity>] [--first-seen-after <ISO8601>] [--cursor <cursor>] [--limit <n>] [--auth-file <path>] [--json]",
29077
29564
  " debugbundle inspect <incident-id> [--source <local|cloud>] [--auth-file <path>] [--json]",
29078
29565
  " debugbundle explain <incident-id> [--source <local|cloud>] [--auth-file <path>] [--json]",
29079
29566
  " debugbundle resolve <incident-id> [incident-id ...] [--source <local|cloud>] [--auth-file <path>] [--json]",
@@ -32379,18 +32866,44 @@ function mapErrorToExitCode12(error) {
32379
32866
  }
32380
32867
  return 1;
32381
32868
  }
32382
- function formatMatcher(rule) {
32869
+ function formatMatcherFromValue(matcher) {
32383
32870
  const parts = [];
32384
- const matcher = rule.matcher;
32385
32871
  if (matcher.event_types !== void 0) {
32386
32872
  parts.push(`event_types=${matcher.event_types.join(",")}`);
32387
32873
  }
32388
32874
  if (matcher.browser_event_kind !== void 0) {
32389
32875
  parts.push(`browser_event_kind=${matcher.browser_event_kind}`);
32390
32876
  }
32877
+ if (matcher.browser_event_opaque !== void 0) {
32878
+ parts.push(`browser_event_opaque=${String(matcher.browser_event_opaque)}`);
32879
+ }
32880
+ if (matcher.client_kind !== void 0) {
32881
+ parts.push(`client_kind=${matcher.client_kind}`);
32882
+ }
32883
+ if (matcher.bot_family !== void 0) {
32884
+ parts.push(`bot_family=${matcher.bot_family}`);
32885
+ }
32886
+ if (matcher.services !== void 0) {
32887
+ parts.push(`services=${matcher.services.join(",")}`);
32888
+ }
32889
+ if (matcher.environments !== void 0) {
32890
+ parts.push(`environments=${matcher.environments.join(",")}`);
32891
+ }
32892
+ if (matcher.message_equals !== void 0) {
32893
+ parts.push(`message_equals=${JSON.stringify(matcher.message_equals)}`);
32894
+ }
32895
+ if (matcher.message_contains !== void 0) {
32896
+ parts.push(`message_contains=${JSON.stringify(matcher.message_contains)}`);
32897
+ }
32898
+ if (matcher.error_name !== void 0) {
32899
+ parts.push(`error_name=${matcher.error_name}`);
32900
+ }
32391
32901
  if (matcher.resource_url?.host !== void 0) {
32392
32902
  parts.push(`resource_host=${matcher.resource_url.host}`);
32393
32903
  }
32904
+ if (matcher.resource_url?.path_equals !== void 0) {
32905
+ parts.push(`resource_path=${matcher.resource_url.path_equals}`);
32906
+ }
32394
32907
  if (matcher.request_url?.path_equals !== void 0) {
32395
32908
  parts.push(`request_path=${matcher.request_url.path_equals}`);
32396
32909
  }
@@ -32402,6 +32915,9 @@ function formatMatcher(rule) {
32402
32915
  }
32403
32916
  return parts.length > 0 ? parts.join(" ") : "matcher=custom";
32404
32917
  }
32918
+ function formatMatcher(rule) {
32919
+ return formatMatcherFromValue(rule.matcher);
32920
+ }
32405
32921
  function formatRule(rule) {
32406
32922
  const action = rule.action === "sample" ? `${rule.action}:${rule.sample_rate ?? "?"}:${rule.sample_event_class ?? "?"}` : rule.action;
32407
32923
  return [
@@ -32422,7 +32938,10 @@ function formatSuggestionResponse(response) {
32422
32938
  return response.suggestions.map(
32423
32939
  (suggestion) => [
32424
32940
  `${suggestion.suggestion_id} ${suggestion.recommended_action} ${suggestion.confidence} ${suggestion.label}`,
32425
- suggestion.reason
32941
+ suggestion.reason,
32942
+ `matcher: ${formatMatcherFromValue(suggestion.rule.matcher)}`,
32943
+ `requires_confirmation: ${String(suggestion.requires_confirmation)}`,
32944
+ `apply: debugbundle capture-rule create-from-suggestion <incident-id> --suggestion-id ${suggestion.suggestion_id}`
32426
32945
  ].join("\n")
32427
32946
  ).join("\n\n");
32428
32947
  }
@@ -34739,7 +35258,7 @@ async function handleCaptureRuleCommand2(parsedArgv, dependencies) {
34739
35258
  // package.json
34740
35259
  var package_default = {
34741
35260
  name: "@debugbundle/cli",
34742
- version: "1.2.0",
35261
+ version: "1.4.0",
34743
35262
  private: false,
34744
35263
  description: "Command-line interface for DebugBundle",
34745
35264
  license: "AGPL-3.0-only",
@@ -35030,7 +35549,7 @@ ${formatUsage()}`
35030
35549
  return await (dependencies.whoamiCommand ?? whoamiCommand)(appendCommonAuthOptions(parsedArgv, {}));
35031
35550
  }
35032
35551
  if (command === "incidents") {
35033
- expectNoUnknownOptions(parsedArgv, ["auth-file", "json", "source", "project-id", "environment", "service", "status", "severity", "cursor", "limit"]);
35552
+ expectNoUnknownOptions(parsedArgv, ["auth-file", "json", "source", "project-id", "environment", "service", "status", "severity", "first-seen-after", "cursor", "limit"]);
35034
35553
  ensureNoExtraPositionals(parsedArgv, 1);
35035
35554
  const input2 = appendCommonAuthOptions(parsedArgv, {});
35036
35555
  const source = readRetrievalSource(parsedArgv);
@@ -35057,6 +35576,10 @@ ${formatUsage()}`
35057
35576
  if (severity !== void 0) {
35058
35577
  input2.severity = severity;
35059
35578
  }
35579
+ const firstSeenAfter = readStringOption(parsedArgv, "first-seen-after");
35580
+ if (firstSeenAfter !== void 0) {
35581
+ input2.firstSeenAfter = firstSeenAfter;
35582
+ }
35060
35583
  const cursor = readStringOption(parsedArgv, "cursor");
35061
35584
  if (cursor !== void 0) {
35062
35585
  input2.cursor = cursor;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@debugbundle/cli",
3
- "version": "1.2.0",
3
+ "version": "1.4.0",
4
4
  "private": false,
5
5
  "description": "Command-line interface for DebugBundle",
6
6
  "license": "AGPL-3.0-only",