@debugbundle/cli 0.1.2 → 0.1.4

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 +660 -78
  2. package/package.json +1 -1
package/dist/main.cjs CHANGED
@@ -14483,6 +14483,8 @@ var CaptureBreadcrumbsValues = ["local_only", "exception_only", "standalone"];
14483
14483
  var CaptureBreadcrumbsSchema = external_exports.enum(CaptureBreadcrumbsValues);
14484
14484
  var CaptureProbeEventsValues = ["buffer_only", "standalone_when_activated"];
14485
14485
  var CaptureProbeEventsSchema = external_exports.enum(CaptureProbeEventsValues);
14486
+ var RequestSignalClassificationValues = ["incident_signal", "context_signal"];
14487
+ var RequestSignalClassificationSchema = external_exports.enum(RequestSignalClassificationValues);
14486
14488
  var CapturePolicySchema = external_exports.object({
14487
14489
  project_id: external_exports.string().uuid(),
14488
14490
  preset: CapturePresetSchema,
@@ -14499,6 +14501,55 @@ var CapturePolicyUpdateSchema = external_exports.object({
14499
14501
  capture_breadcrumbs: CaptureBreadcrumbsSchema.nullable().optional(),
14500
14502
  capture_probe_events: CaptureProbeEventsSchema.nullable().optional()
14501
14503
  });
14504
+ var BALANCED_IMMEDIATE_REQUEST_STATUSES = /* @__PURE__ */ new Set([408, 423, 424, 425, 429]);
14505
+ var INVESTIGATIVE_IMMEDIATE_REQUEST_STATUSES = /* @__PURE__ */ new Set([...BALANCED_IMMEDIATE_REQUEST_STATUSES, 409]);
14506
+ var BALANCED_STANDARD_ANOMALY_STATUSES = /* @__PURE__ */ new Set([401, 403, 404, 409, 422]);
14507
+ var BALANCED_HIGH_VOLUME_ANOMALY_STATUSES = /* @__PURE__ */ new Set([400, 410]);
14508
+ var INVESTIGATIVE_ANOMALY_STATUSES = /* @__PURE__ */ new Set([...BALANCED_STANDARD_ANOMALY_STATUSES, ...BALANCED_HIGH_VOLUME_ANOMALY_STATUSES]);
14509
+ function classifyRequestStatus(input) {
14510
+ const { responseStatus, capturePreset } = input;
14511
+ if (responseStatus === null || !Number.isFinite(responseStatus)) {
14512
+ return "context_signal";
14513
+ }
14514
+ if (responseStatus >= 500) {
14515
+ return "incident_signal";
14516
+ }
14517
+ if (capturePreset === "investigative") {
14518
+ return INVESTIGATIVE_IMMEDIATE_REQUEST_STATUSES.has(responseStatus) ? "incident_signal" : "context_signal";
14519
+ }
14520
+ if (capturePreset === "balanced") {
14521
+ return BALANCED_IMMEDIATE_REQUEST_STATUSES.has(responseStatus) ? "incident_signal" : "context_signal";
14522
+ }
14523
+ return "context_signal";
14524
+ }
14525
+ function getRequestAnomalyThreshold(input) {
14526
+ const { responseStatus, capturePreset } = input;
14527
+ if (responseStatus === null || !Number.isFinite(responseStatus) || responseStatus < 400 || responseStatus >= 500) {
14528
+ return null;
14529
+ }
14530
+ if (capturePreset === "minimal") {
14531
+ return null;
14532
+ }
14533
+ if (capturePreset === "investigative") {
14534
+ return INVESTIGATIVE_ANOMALY_STATUSES.has(responseStatus) ? {
14535
+ minimum_occurrences_5m: 8,
14536
+ minimum_ratio_5m_to_1h: 2
14537
+ } : null;
14538
+ }
14539
+ if (BALANCED_STANDARD_ANOMALY_STATUSES.has(responseStatus)) {
14540
+ return {
14541
+ minimum_occurrences_5m: 20,
14542
+ minimum_ratio_5m_to_1h: 3
14543
+ };
14544
+ }
14545
+ if (BALANCED_HIGH_VOLUME_ANOMALY_STATUSES.has(responseStatus)) {
14546
+ return {
14547
+ minimum_occurrences_5m: 50,
14548
+ minimum_ratio_5m_to_1h: 5
14549
+ };
14550
+ }
14551
+ return null;
14552
+ }
14502
14553
 
14503
14554
  // ../../packages/shared-types/src/index.ts
14504
14555
  function createUuidV4() {
@@ -15124,7 +15175,7 @@ function buildCliReference() {
15124
15175
  "- `debugbundle ingest <file> --format <format> [--json]`",
15125
15176
  "- `debugbundle watch --log <file> --format <format> [--json]`",
15126
15177
  "- `debugbundle watch --cloud --log <file> --format <format> [--json]`",
15127
- "- `debugbundle process [--json]`",
15178
+ "- `debugbundle process [--preset <minimal|balanced|investigative>] [--json]`",
15128
15179
  "",
15129
15180
  "## Investigation",
15130
15181
  "",
@@ -15670,7 +15721,7 @@ var import_node_path4 = require("node:path");
15670
15721
 
15671
15722
  // ../../packages/retrieval-client/src/index.ts
15672
15723
  var IncidentReasonSchema = external_exports.object({
15673
- kind: external_exports.enum(["backend_exception", "frontend_exception", "request_failure_5xx", "error_log"]),
15724
+ kind: external_exports.enum(["backend_exception", "frontend_exception", "request_failure", "error_log"]),
15674
15725
  description: external_exports.string(),
15675
15726
  event_type: external_exports.enum(["backend_exception", "frontend_exception", "request_event", "log_event"]),
15676
15727
  event_class: external_exports.literal("incident_signal"),
@@ -16091,7 +16142,8 @@ function buildRedactionRecord(bundleBody) {
16091
16142
  function buildVisibilityRecord(input) {
16092
16143
  const routeTarget = input.primarySignal.route_template ?? input.primarySignal.request_path;
16093
16144
  const matchedFields = input.incident.matched_fields.length === 0 ? "none" : input.incident.matched_fields.join(", ");
16094
- const grouping = input.primarySignal.kind === "request_failure_5xx" && input.primarySignal.request_method !== null && routeTarget !== null ? `Repeated 5xx request failures with the same normalized route template, request method, response status, service, and environment reuse this incident fingerprint. This incident currently groups ${input.primarySignal.request_method} ${routeTarget} with matched fields ${matchedFields}.` : `This incident groups repeated failures by fingerprint version ${input.incident.fingerprint_version} inside the service and environment boundary, with matched fields ${matchedFields}.`;
16145
+ const isRequestAnomaly = input.incident.matched_fields.includes("request_anomaly");
16146
+ const grouping = input.primarySignal.kind === "request_failure" && input.primarySignal.request_method !== null && routeTarget !== null ? isRequestAnomaly ? `Repeated request-anomaly incidents with the same normalized route template, request method, response status, service, and environment reuse this incident fingerprint once the anomaly threshold fires. This incident currently groups ${input.primarySignal.request_method} ${routeTarget} with matched fields ${matchedFields}.` : `Repeated request-failure incidents with the same normalized route template, request method, response status, service, and environment reuse this incident fingerprint. This incident currently groups ${input.primarySignal.request_method} ${routeTarget} with matched fields ${matchedFields}.` : `This incident groups repeated failures by fingerprint version ${input.incident.fingerprint_version} inside the service and environment boundary, with matched fields ${matchedFields}.`;
16095
16147
  const spikeLead = input.incident.spike_detected_at === void 0 || input.incident.spike_detected_at === null ? "This incident is not currently marked as spiking." : `This incident was marked as spiking at ${input.incident.spike_detected_at}.`;
16096
16148
  return {
16097
16149
  grouping,
@@ -16102,15 +16154,16 @@ function buildVisibilityRecord(input) {
16102
16154
  }
16103
16155
  function buildSuggestedNextChecks(input) {
16104
16156
  const suggestions = [];
16157
+ const isRequestAnomaly = input.incident.matched_fields.includes("request_anomaly");
16105
16158
  if (input.bundle.status === "pending") {
16106
16159
  suggestions.push("Wait for bundle generation to finish, then rerun the incident context command.");
16107
16160
  } else if (input.bundle.status === "failed") {
16108
16161
  suggestions.push("Inspect bundle generation status or retry bundle retrieval to recover missing context.");
16109
16162
  }
16110
16163
  const routeTarget = input.primarySignal.route_template ?? input.primarySignal.request_path;
16111
- if (input.primarySignal.request_method !== null && input.primarySignal.response_status !== null && input.primarySignal.response_status >= 500) {
16164
+ if (input.primarySignal.kind === "request_failure" && input.primarySignal.request_method !== null && routeTarget !== null) {
16112
16165
  suggestions.push(
16113
- `Inspect the ${input.primarySignal.request_method} ${routeTarget ?? "request"} handler behind this 5xx path.`
16166
+ isRequestAnomaly ? `Inspect the ${input.primarySignal.request_method} ${routeTarget} handler behind this repeated request-anomaly path.` : `Inspect the ${input.primarySignal.request_method} ${routeTarget} handler behind this request-failure path.`
16114
16167
  );
16115
16168
  }
16116
16169
  const firstApplicationFrame = input.primarySignal.first_application_frame;
@@ -16199,12 +16252,13 @@ function deriveIncidentReasonFromSignal(input) {
16199
16252
  };
16200
16253
  case "request_event": {
16201
16254
  const responseStatus = typeof input.response_status === "number" && Number.isFinite(input.response_status) ? input.response_status : null;
16255
+ const isRequestAnomaly = input.request_anomaly === true;
16202
16256
  return {
16203
- kind: "request_failure_5xx",
16204
- description: responseStatus !== null && responseStatus >= 500 ? `request_event response_status=${responseStatus} matched the 5xx request incident rule` : "request_event matched the 5xx request incident rule",
16257
+ kind: "request_failure",
16258
+ description: isRequestAnomaly ? responseStatus !== null ? `request_event response_status=${responseStatus} crossed the repeated request anomaly threshold` : "request_event crossed the repeated request anomaly threshold" : responseStatus !== null ? `request_event response_status=${responseStatus} matched the immediate request failure incident rule` : "request_event matched the immediate request failure incident rule",
16205
16259
  event_type: "request_event",
16206
16260
  event_class: "incident_signal",
16207
- matched_policy: "5xx request failures bypass capture_request_events suppression"
16261
+ matched_policy: isRequestAnomaly ? "Repeated contextual request failures crossed the request anomaly threshold" : "Immediate request failure statuses bypass capture_request_events suppression"
16208
16262
  };
16209
16263
  }
16210
16264
  case "log_event": {
@@ -16511,7 +16565,7 @@ function getRequestResponseStatus(payload) {
16511
16565
  const status = payload?.["response_status"];
16512
16566
  return typeof status === "number" && Number.isFinite(status) ? status : null;
16513
16567
  }
16514
- function classifyEvent(eventType, logLevel, probeActivationId, payload) {
16568
+ function classifyEvent(eventType, logLevel, probeActivationId, payload, capturePreset = "minimal") {
16515
16569
  switch (eventType) {
16516
16570
  case "backend_exception":
16517
16571
  case "frontend_exception":
@@ -16523,10 +16577,7 @@ function classifyEvent(eventType, logLevel, probeActivationId, payload) {
16523
16577
  return "context_signal";
16524
16578
  case "request_event": {
16525
16579
  const responseStatus = getRequestResponseStatus(payload);
16526
- if (responseStatus !== null && responseStatus >= 500) {
16527
- return "incident_signal";
16528
- }
16529
- return "context_signal";
16580
+ return classifyRequestStatus({ responseStatus, capturePreset });
16530
16581
  }
16531
16582
  case "frontend_breadcrumb":
16532
16583
  case "deploy_metadata":
@@ -16596,6 +16647,39 @@ var STORAGE_SCHEMA_MIGRATIONS = [
16596
16647
  ON github_device_authorizations (expires_at)
16597
16648
  `
16598
16649
  ]
16650
+ }),
16651
+ defineStorageSchemaMigration({
16652
+ id: "202605130001_allow_synthetic_webhook_test_deliveries_without_incident_fk",
16653
+ description: "Allow webhook test deliveries to persist without requiring a backing incidents row.",
16654
+ statements: [
16655
+ "ALTER TABLE webhook_deliveries ALTER COLUMN incident_id DROP NOT NULL"
16656
+ ]
16657
+ }),
16658
+ defineStorageSchemaMigration({
16659
+ id: "202605130002_add_slack_destinations",
16660
+ description: "Add reusable encrypted Slack alert destinations scoped to organizations.",
16661
+ statements: [
16662
+ `
16663
+ CREATE TABLE IF NOT EXISTS slack_destinations (
16664
+ id uuid PRIMARY KEY,
16665
+ organization_id uuid NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
16666
+ slack_team_id text NOT NULL,
16667
+ slack_team_name text,
16668
+ slack_channel_id text NOT NULL,
16669
+ slack_channel_name text,
16670
+ webhook_url_ciphertext text NOT NULL,
16671
+ installed_by_member_id uuid REFERENCES users(id) ON DELETE SET NULL,
16672
+ is_active boolean NOT NULL DEFAULT true,
16673
+ created_at timestamptz NOT NULL DEFAULT now(),
16674
+ updated_at timestamptz NOT NULL DEFAULT now(),
16675
+ UNIQUE (organization_id, slack_team_id, slack_channel_id)
16676
+ )
16677
+ `,
16678
+ `
16679
+ CREATE INDEX IF NOT EXISTS slack_destinations_org_active_idx
16680
+ ON slack_destinations (organization_id, is_active, created_at)
16681
+ `
16682
+ ]
16599
16683
  })
16600
16684
  ];
16601
16685
 
@@ -16671,7 +16755,11 @@ function parseLocalIncident(candidate) {
16671
16755
  }
16672
16756
  const serviceRuntime = candidate["service_runtime"];
16673
16757
  const serviceFramework = candidate["service_framework"];
16674
- const incidentReason = deriveIncidentReasonFromSourceEventTypes(candidate["source_event_types"]);
16758
+ const incidentReason = candidate["matched_fields"].includes("request_anomaly") ? deriveIncidentReasonFromSignal({
16759
+ event_type: "request_event",
16760
+ event_class: "incident_signal",
16761
+ request_anomaly: true
16762
+ }) : deriveIncidentReasonFromSourceEventTypes(candidate["source_event_types"]);
16675
16763
  if (serviceRuntime !== null && typeof serviceRuntime !== "string") {
16676
16764
  throw createReadError(400, "invalid_local_state");
16677
16765
  }
@@ -18255,7 +18343,7 @@ function createWeeklyReportApi(client) {
18255
18343
  );
18256
18344
  },
18257
18345
  async createWeeklyReportChannel(input) {
18258
- const config = input.channel === "email" ? { to: input.config.to } : { webhook_url: input.config.webhookUrl };
18346
+ const config = input.channel === "email" ? { to: input.config.to } : "webhookUrl" in input.config ? { webhook_url: input.config.webhookUrl } : { slack_destination_id: input.config.slackDestinationId };
18259
18347
  return expectChannel(
18260
18348
  client.request({
18261
18349
  method: "POST",
@@ -18278,7 +18366,7 @@ function createWeeklyReportApi(client) {
18278
18366
  async updateWeeklyReportChannel(input) {
18279
18367
  const body = {};
18280
18368
  if (input.config !== void 0) {
18281
- body["config"] = "to" in input.config ? { to: input.config.to } : { webhook_url: input.config.webhookUrl };
18369
+ body["config"] = "to" in input.config ? { to: input.config.to } : "webhookUrl" in input.config ? { webhook_url: input.config.webhookUrl } : { slack_destination_id: input.config.slackDestinationId };
18282
18370
  }
18283
18371
  if (input.schedule !== void 0) {
18284
18372
  body["schedule"] = {
@@ -18747,6 +18835,121 @@ function createGitHubManagementApi(client) {
18747
18835
  };
18748
18836
  }
18749
18837
 
18838
+ // ../../packages/slack-client/src/index.ts
18839
+ var SlackDestinationRecordSchema = external_exports.object({
18840
+ slack_destination_id: external_exports.string(),
18841
+ organization_id: external_exports.string(),
18842
+ slack_team_id: external_exports.string(),
18843
+ slack_team_name: external_exports.string().nullable(),
18844
+ slack_channel_id: external_exports.string(),
18845
+ slack_channel_name: external_exports.string().nullable(),
18846
+ installed_by_member_id: external_exports.string().nullable(),
18847
+ is_active: external_exports.boolean(),
18848
+ created_at: external_exports.string(),
18849
+ updated_at: external_exports.string()
18850
+ }).strict();
18851
+ var SlackDestinationListResponseSchema = external_exports.object({
18852
+ destinations: external_exports.array(SlackDestinationRecordSchema)
18853
+ }).strict();
18854
+ var SlackInstallUrlResponseSchema = external_exports.object({
18855
+ install_url: external_exports.string().url()
18856
+ }).strict();
18857
+ var SlackDestinationTestResponseSchema = external_exports.object({
18858
+ delivered: external_exports.boolean()
18859
+ }).strict();
18860
+ var ApiErrorResponseSchema9 = external_exports.object({
18861
+ error: external_exports.string()
18862
+ }).strict();
18863
+ var SlackApiError = class extends Error {
18864
+ status;
18865
+ code;
18866
+ constructor(status, code) {
18867
+ super(`slack_api_error: ${status}:${code}`);
18868
+ this.status = status;
18869
+ this.code = code;
18870
+ }
18871
+ };
18872
+ function parseApiError9(status, body) {
18873
+ const parsed = ApiErrorResponseSchema9.safeParse(body);
18874
+ if (!parsed.success) {
18875
+ throw new SlackApiError(status, "unknown_error");
18876
+ }
18877
+ throw new SlackApiError(status, parsed.data.error);
18878
+ }
18879
+ function buildQuery4(input) {
18880
+ const params = new URLSearchParams();
18881
+ for (const [key, value] of Object.entries(input)) {
18882
+ if (value !== void 0) {
18883
+ params.set(key, String(value));
18884
+ }
18885
+ }
18886
+ const query = params.toString();
18887
+ return query.length === 0 ? "" : `?${query}`;
18888
+ }
18889
+ function createSlackApi(client) {
18890
+ return {
18891
+ async getSlackInstallUrl(input) {
18892
+ const response = await client.request({
18893
+ method: "GET",
18894
+ path: `/v1/slack/app/install-url${buildQuery4({
18895
+ project_id: input.projectId,
18896
+ return_to: input.returnTo
18897
+ })}`,
18898
+ bearerToken: input.bearerToken
18899
+ });
18900
+ if (response.status < 200 || response.status >= 300) {
18901
+ parseApiError9(response.status, response.body);
18902
+ }
18903
+ const parsed = SlackInstallUrlResponseSchema.safeParse(response.body);
18904
+ if (!parsed.success) {
18905
+ throw new SlackApiError(response.status, "invalid_response_shape");
18906
+ }
18907
+ return parsed.data.install_url;
18908
+ },
18909
+ async listSlackDestinations(input) {
18910
+ const response = await client.request({
18911
+ method: "GET",
18912
+ path: `/v1/projects/${input.projectId}/slack/destinations`,
18913
+ bearerToken: input.bearerToken
18914
+ });
18915
+ if (response.status < 200 || response.status >= 300) {
18916
+ parseApiError9(response.status, response.body);
18917
+ }
18918
+ const parsed = SlackDestinationListResponseSchema.safeParse(response.body);
18919
+ if (!parsed.success) {
18920
+ throw new SlackApiError(response.status, "invalid_response_shape");
18921
+ }
18922
+ return parsed.data.destinations;
18923
+ },
18924
+ async testSlackDestination(input) {
18925
+ const response = await client.request({
18926
+ method: "POST",
18927
+ path: `/v1/projects/${input.projectId}/slack/destinations/${input.destinationId}/test`,
18928
+ bearerToken: input.bearerToken
18929
+ });
18930
+ if (response.status < 200 || response.status >= 300) {
18931
+ parseApiError9(response.status, response.body);
18932
+ }
18933
+ const parsed = SlackDestinationTestResponseSchema.safeParse(response.body);
18934
+ if (!parsed.success || parsed.data.delivered !== true) {
18935
+ throw new SlackApiError(response.status, "invalid_response_shape");
18936
+ }
18937
+ return { delivered: true };
18938
+ },
18939
+ async deleteSlackDestination(input) {
18940
+ const response = await client.request({
18941
+ method: "DELETE",
18942
+ path: `/v1/projects/${input.projectId}/slack/destinations/${input.destinationId}`,
18943
+ bearerToken: input.bearerToken
18944
+ });
18945
+ if (response.status < 200 || response.status >= 300) {
18946
+ parseApiError9(response.status, response.body);
18947
+ }
18948
+ return { slack_destination_id: input.destinationId };
18949
+ }
18950
+ };
18951
+ }
18952
+
18750
18953
  // src/auth-context.ts
18751
18954
  function normalizeBaseUrl(baseUrl) {
18752
18955
  return baseUrl.endsWith("/") ? baseUrl.slice(0, -1) : baseUrl;
@@ -18922,6 +19125,29 @@ async function createAuthenticatedWeeklyReportApi(input, dependencies) {
18922
19125
  api: createApi(httpClient)
18923
19126
  };
18924
19127
  }
19128
+ async function createAuthenticatedSlackApi(input, dependencies) {
19129
+ const readAuthState = dependencies?.readAuthState ?? readCliAuthState;
19130
+ const authStateInput = {};
19131
+ if (input.authFilePath !== void 0) {
19132
+ authStateInput.authFilePath = input.authFilePath;
19133
+ }
19134
+ const authState = await readAuthState(authStateInput);
19135
+ const createHttpClient = dependencies?.createHttpClient ?? ((clientInput) => {
19136
+ const httpClientDependencies = {};
19137
+ if (dependencies?.fetchImpl !== void 0) {
19138
+ httpClientDependencies.fetchImpl = dependencies.fetchImpl;
19139
+ }
19140
+ return createCliHttpClient(clientInput, httpClientDependencies);
19141
+ });
19142
+ const httpClient = createHttpClient({
19143
+ baseUrl: authState.base_url
19144
+ });
19145
+ const createApi = dependencies?.createApi ?? createSlackApi;
19146
+ return {
19147
+ authState,
19148
+ api: createApi(httpClient)
19149
+ };
19150
+ }
18925
19151
  async function createAuthenticatedProjectManagementApi(input, dependencies) {
18926
19152
  const readAuthState = dependencies?.readAuthState ?? readCliAuthState;
18927
19153
  const authStateInput = {};
@@ -19107,7 +19333,7 @@ var LoginCommandInputSchema = external_exports.object({
19107
19333
  });
19108
19334
  }
19109
19335
  });
19110
- var ApiErrorResponseSchema9 = external_exports.object({
19336
+ var ApiErrorResponseSchema10 = external_exports.object({
19111
19337
  error: external_exports.string()
19112
19338
  }).strict();
19113
19339
  var DeviceStartResponseSchema = external_exports.object({
@@ -19283,7 +19509,7 @@ async function requestJson(input, dependencies) {
19283
19509
  });
19284
19510
  const body = await parseResponseBody2(response);
19285
19511
  if (response.status < 200 || response.status >= 300) {
19286
- const parsedError = ApiErrorResponseSchema9.safeParse(body);
19512
+ const parsedError = ApiErrorResponseSchema10.safeParse(body);
19287
19513
  throw new LoginApiError(response.status, parsedError.success ? parsedError.data.error : "unknown_error");
19288
19514
  }
19289
19515
  return body;
@@ -19966,7 +20192,7 @@ function buildPrivacyPreview() {
19966
20192
  sample_event_type: sampleEvent.event_type,
19967
20193
  sample_event_class: sampleEventClass,
19968
20194
  sample_can_create_incident: sampleEventClass === "incident_signal",
19969
- incident_rule: "request_event with response_status >= 500 is classified as an incident_signal",
20195
+ incident_rule: "request_event incident classification follows the resolved capture preset: 5xx always create incidents, balanced also promotes 408/423/424/425/429, and investigative also promotes 409.",
19970
20196
  redacted_fields,
19971
20197
  omitted_fields: [],
19972
20198
  retained_metadata: {
@@ -21668,11 +21894,17 @@ var EVENT_TYPE_SET = new Set(EventTypeValues);
21668
21894
  function isEventType(value) {
21669
21895
  return typeof value === "string" && EVENT_TYPE_SET.has(value);
21670
21896
  }
21671
- function inferSeverity2(eventType) {
21672
- if (eventType === "backend_exception" || eventType === "frontend_exception") {
21897
+ function inferSeverity2(event, capturePreset, incidentKind = "immediate") {
21898
+ if (incidentKind === "request_anomaly") {
21899
+ return "medium";
21900
+ }
21901
+ if (event.event_type === "request_event") {
21902
+ return classifyRequestStatus({ responseStatus: event.payload.response_status, capturePreset }) === "incident_signal" ? "high" : "low";
21903
+ }
21904
+ if (event.event_type === "backend_exception" || event.event_type === "frontend_exception") {
21673
21905
  return "high";
21674
21906
  }
21675
- if (eventType === "error_suppressed") {
21907
+ if (event.event_type === "error_suppressed") {
21676
21908
  return "medium";
21677
21909
  }
21678
21910
  return "low";
@@ -21696,15 +21928,17 @@ function compareEventEnvelopes(left, right) {
21696
21928
  }
21697
21929
  return left.event_id.localeCompare(right.event_id);
21698
21930
  }
21699
- function classifyEnvelope(envelope) {
21931
+ function classifyEnvelope(envelope, capturePreset) {
21700
21932
  return classifyEvent(
21701
21933
  envelope.event_type,
21702
21934
  envelope.event_type === "log_event" ? envelope.payload.level : void 0,
21703
- envelope.event_type === "probe_event" ? envelope.payload.activation_id : void 0
21935
+ envelope.event_type === "probe_event" ? envelope.payload.activation_id : void 0,
21936
+ envelope.payload,
21937
+ capturePreset
21704
21938
  );
21705
21939
  }
21706
- function isIncidentSignalEnvelope(envelope) {
21707
- return classifyEnvelope(envelope) === "incident_signal";
21940
+ function isIncidentSignalEnvelope(envelope, capturePreset) {
21941
+ return classifyEnvelope(envelope, capturePreset) === "incident_signal";
21708
21942
  }
21709
21943
  function getTraceId(envelope) {
21710
21944
  return envelope.correlation?.trace_id ?? null;
@@ -21749,7 +21983,10 @@ function mergeAggregateGroup(aggregates) {
21749
21983
  newEvents: [...canonicalAggregate.newEvents],
21750
21984
  mergedIncidentIds: new Set(canonicalAggregate.mergedIncidentIds),
21751
21985
  signalEventTypes: new Set(canonicalAggregate.signalEventTypes),
21752
- traceIds: new Set(canonicalAggregate.traceIds)
21986
+ traceIds: new Set(canonicalAggregate.traceIds),
21987
+ title: canonicalAggregate.title,
21988
+ kind: canonicalAggregate.kind,
21989
+ severity: canonicalAggregate.severity
21753
21990
  });
21754
21991
  }
21755
21992
  function hashIdentifier(parts, prefix, length) {
@@ -21775,6 +22012,111 @@ function mergeSourceEvents(existingEvents, nextEvents) {
21775
22012
  }
21776
22013
  return [...merged.values()].sort(compareEventEnvelopes);
21777
22014
  }
22015
+ function stableJson2(value) {
22016
+ if (value === null || typeof value !== "object") {
22017
+ return JSON.stringify(value);
22018
+ }
22019
+ if (Array.isArray(value)) {
22020
+ return `[${value.map((entry) => stableJson2(entry)).join(",")}]`;
22021
+ }
22022
+ const record = value;
22023
+ const keys = Object.keys(record).sort();
22024
+ return `{${keys.map((key) => `${JSON.stringify(key)}:${stableJson2(record[key])}`).join(",")}}`;
22025
+ }
22026
+ function buildRequestAnomalyFingerprint(input) {
22027
+ return (0, import_node_crypto4.createHash)("sha256").update(
22028
+ stableJson2({
22029
+ kind: "request_status_anomaly",
22030
+ project_id: input.projectId,
22031
+ service_name: input.serviceName,
22032
+ environment: input.environment,
22033
+ method: input.method,
22034
+ route_template: input.routeTemplate,
22035
+ response_status: input.responseStatus
22036
+ })
22037
+ ).digest("hex");
22038
+ }
22039
+ function buildRequestAnomalyTitle(input) {
22040
+ return `Request anomaly: ${input.method} ${input.routeTemplate} returned ${input.responseStatus} repeatedly`;
22041
+ }
22042
+ function toUnixSeconds(occurredAt) {
22043
+ return Math.floor(new Date(occurredAt).getTime() / 1e3);
22044
+ }
22045
+ function countOccurrencesInWindow(events, windowSeconds) {
22046
+ const latestEvent = events.at(-1);
22047
+ if (latestEvent === void 0) {
22048
+ return 0;
22049
+ }
22050
+ const latestOccurredAt = toUnixSeconds(latestEvent.occurred_at);
22051
+ const lowerBound = latestOccurredAt - windowSeconds + 1;
22052
+ return events.filter((event) => {
22053
+ const occurredAt = toUnixSeconds(event.occurred_at);
22054
+ return occurredAt >= lowerBound && occurredAt <= latestOccurredAt;
22055
+ }).length;
22056
+ }
22057
+ function passesRequestAnomalyThreshold(events, threshold) {
22058
+ const occurrences5m = countOccurrencesInWindow(events, 5 * 60);
22059
+ const occurrences1h = countOccurrencesInWindow(events, 60 * 60);
22060
+ const baseline1hPer5m = occurrences1h / 12;
22061
+ const ratio = occurrences5m / Math.max(baseline1hPer5m, 1);
22062
+ return occurrences5m >= threshold.minimum_occurrences_5m && ratio >= threshold.minimum_ratio_5m_to_1h;
22063
+ }
22064
+ function collectRequestAnomalyAggregates(batches, capturePreset) {
22065
+ const grouped = /* @__PURE__ */ new Map();
22066
+ for (const batch of batches) {
22067
+ for (const event of batch.events) {
22068
+ if (event.event_type !== "request_event" || classifyEnvelope(event, capturePreset) !== "context_signal") {
22069
+ continue;
22070
+ }
22071
+ const normalizedEvent = normalizeEvent(event);
22072
+ const responseStatus = normalizedEvent.http_status;
22073
+ const method = normalizedEvent.http_method;
22074
+ const routeTemplate = normalizedEvent.route_template;
22075
+ const threshold = getRequestAnomalyThreshold({ responseStatus, capturePreset });
22076
+ if (threshold === null || responseStatus === null || method === null || routeTemplate === null) {
22077
+ continue;
22078
+ }
22079
+ const projectId = requireProjectId(event);
22080
+ const incidentFingerprint = buildRequestAnomalyFingerprint({
22081
+ projectId,
22082
+ serviceName: event.service.name,
22083
+ environment: event.service.environment,
22084
+ method,
22085
+ routeTemplate,
22086
+ responseStatus
22087
+ });
22088
+ const incidentId = deriveIncidentId(projectId, event.service.name, event.service.environment, incidentFingerprint);
22089
+ const aggregate = grouped.get(incidentId) ?? {
22090
+ incidentId,
22091
+ projectId,
22092
+ serviceName: event.service.name,
22093
+ environment: event.service.environment,
22094
+ fingerprint: incidentFingerprint,
22095
+ matchedFields: /* @__PURE__ */ new Set(["request_anomaly", "route_template", "http_method", "http_status", "environment"]),
22096
+ newEvents: [],
22097
+ mergedIncidentIds: /* @__PURE__ */ new Set([incidentId]),
22098
+ signalEventTypes: /* @__PURE__ */ new Set(["request_event"]),
22099
+ traceIds: /* @__PURE__ */ new Set(),
22100
+ title: buildRequestAnomalyTitle({ method, routeTemplate, responseStatus }),
22101
+ kind: "request_anomaly",
22102
+ severity: "medium"
22103
+ };
22104
+ aggregate.newEvents.push(event);
22105
+ grouped.set(incidentId, aggregate);
22106
+ }
22107
+ }
22108
+ return [...grouped.values()].filter((aggregate) => {
22109
+ const latestEvent = aggregate.newEvents.at(-1);
22110
+ if (latestEvent === void 0 || latestEvent.event_type !== "request_event") {
22111
+ return false;
22112
+ }
22113
+ const threshold = getRequestAnomalyThreshold({
22114
+ responseStatus: normalizeEvent(latestEvent).http_status,
22115
+ capturePreset
22116
+ });
22117
+ return threshold !== null && passesRequestAnomalyThreshold(aggregate.newEvents, threshold);
22118
+ }).sort((left, right) => left.incidentId.localeCompare(right.incidentId));
22119
+ }
21778
22120
  function buildBundleContext(incident) {
21779
22121
  return {
21780
22122
  incident_id: incident.incident_id,
@@ -21804,12 +22146,12 @@ function formatServiceSummary(services) {
21804
22146
  }
21805
22147
  function formatProcessOutput(summary) {
21806
22148
  if (!summary.processed) {
21807
- return summary.message ?? "No new events to process.";
22149
+ return summary.message;
21808
22150
  }
21809
22151
  return [
21810
22152
  `Processed ${summary.events_processed} events from ${summary.files_processed} files into ${summary.incidents_processed} incidents.`,
21811
22153
  ...formatServiceSummary(summary.services),
21812
- `Last processed event file: ${summary.last_processed_event_file ?? "none"}`
22154
+ `Last processed event file: ${summary.last_processed_event_file}`
21813
22155
  ].join("\n");
21814
22156
  }
21815
22157
  async function pathExists4(path, stat) {
@@ -22049,6 +22391,7 @@ async function processCommand(input, dependencies = {}) {
22049
22391
  const statePath = (0, import_node_path11.join)(rootDirectory, LOCAL_STATE_FILE_PATH);
22050
22392
  const bundleDirectoryPath = (0, import_node_path11.join)(rootDirectory, LOCAL_BUNDLE_DIRECTORY_PATH3);
22051
22393
  const reproductionDirectoryPath = (0, import_node_path11.join)(rootDirectory, LOCAL_REPRODUCTION_DIRECTORY_PATH2);
22394
+ const capturePreset = input.preset ?? "minimal";
22052
22395
  await mkdir((0, import_node_path11.join)(rootDirectory, ".debugbundle", "local"), { recursive: true });
22053
22396
  await mkdir(bundleDirectoryPath, { recursive: true });
22054
22397
  await mkdir(reproductionDirectoryPath, { recursive: true });
@@ -22056,22 +22399,26 @@ async function processCommand(input, dependencies = {}) {
22056
22399
  const eventFileNames = await pathExists4(eventsDirectoryPath, stat) ? (await readdir(eventsDirectoryPath)).filter((fileName) => fileName.endsWith(".events.json")).sort() : [];
22057
22400
  const lastProcessedEventFile = previousState?.last_processed_event_file ?? null;
22058
22401
  const newEventFileNames = lastProcessedEventFile === null ? eventFileNames : eventFileNames.filter((fileName) => fileName > lastProcessedEventFile);
22059
- if (newEventFileNames.length === 0) {
22402
+ const processAllEventFiles = input.preset !== void 0;
22403
+ const targetEventFileNames = processAllEventFiles ? eventFileNames : newEventFileNames;
22404
+ if (targetEventFileNames.length === 0) {
22060
22405
  const summary2 = buildNoNewEventsSummary(previousState?.last_processed_event_file ?? eventFileNames.at(-1) ?? null);
22061
22406
  return {
22062
22407
  exitCode: 0,
22063
22408
  output: input.json === true ? JSON.stringify(summary2) : formatProcessOutput(summary2)
22064
22409
  };
22065
22410
  }
22066
- const batches = await readEventBatches(eventsDirectoryPath, newEventFileNames, readFile);
22067
- const incidents = new Map(Object.entries(previousState?.incidents ?? {}));
22411
+ const batches = await readEventBatches(eventsDirectoryPath, targetEventFileNames, readFile);
22412
+ const incidents = new Map(
22413
+ processAllEventFiles ? [] : Object.entries(previousState?.incidents ?? {})
22414
+ );
22068
22415
  const aggregates = /* @__PURE__ */ new Map();
22069
22416
  const traceCorrelationGroups = /* @__PURE__ */ new Map();
22070
22417
  let eventsProcessed = 0;
22071
22418
  for (const batch of batches) {
22072
22419
  for (const event of batch.events) {
22073
22420
  eventsProcessed += 1;
22074
- if (!isIncidentSignalEnvelope(event)) {
22421
+ if (!isIncidentSignalEnvelope(event, capturePreset)) {
22075
22422
  continue;
22076
22423
  }
22077
22424
  const normalizedEvent = normalizeEvent(event);
@@ -22088,7 +22435,10 @@ async function processCommand(input, dependencies = {}) {
22088
22435
  newEvents: [],
22089
22436
  mergedIncidentIds: /* @__PURE__ */ new Set([incidentId]),
22090
22437
  signalEventTypes: /* @__PURE__ */ new Set(),
22091
- traceIds: /* @__PURE__ */ new Set()
22438
+ traceIds: /* @__PURE__ */ new Set(),
22439
+ title: normalizedEvent.normalized_message,
22440
+ kind: "immediate",
22441
+ severity: inferSeverity2(event, capturePreset)
22092
22442
  };
22093
22443
  for (const matchedField of inferMatchedFields(normalizedEvent)) {
22094
22444
  aggregate.matchedFields.add(matchedField);
@@ -22155,14 +22505,16 @@ async function processCommand(input, dependencies = {}) {
22155
22505
  mergedAggregatesByRoot.set(rootIncidentId, aggregateGroup);
22156
22506
  }
22157
22507
  const mergedAggregates = [...mergedAggregatesByRoot.values()].map((aggregateGroup) => mergeAggregateGroup(aggregateGroup)).sort((left, right) => left.incidentId.localeCompare(right.incidentId));
22508
+ const requestAnomalyAggregates = input.preset === void 0 ? [] : collectRequestAnomalyAggregates(batches, capturePreset);
22509
+ const finalizedAggregates = [...mergedAggregates, ...requestAnomalyAggregates].sort((left, right) => left.incidentId.localeCompare(right.incidentId));
22158
22510
  const services = /* @__PURE__ */ new Map();
22159
- for (const aggregate of mergedAggregates) {
22511
+ for (const aggregate of finalizedAggregates) {
22160
22512
  const incidentId = aggregate.incidentId;
22161
22513
  const existingIncidents = [...aggregate.mergedIncidentIds].map((mergedIncidentId) => incidents.get(mergedIncidentId)).filter((incident2) => incident2 !== void 0);
22162
22514
  const existing = existingIncidents.find((incident2) => incident2.incident_id === incidentId) ?? existingIncidents[0];
22163
22515
  const existingSourceEvents = existingIncidents.flatMap((incident2) => incident2.source_events);
22164
22516
  const combinedSourceEvents = mergeSourceEvents(existingSourceEvents, aggregate.newEvents);
22165
- const signalEvents = combinedSourceEvents.filter(isIncidentSignalEnvelope);
22517
+ const signalEvents = aggregate.kind === "request_anomaly" ? combinedSourceEvents : combinedSourceEvents.filter((event) => isIncidentSignalEnvelope(event, capturePreset));
22166
22518
  if (signalEvents.length === 0) {
22167
22519
  continue;
22168
22520
  }
@@ -22172,8 +22524,7 @@ async function processCommand(input, dependencies = {}) {
22172
22524
  continue;
22173
22525
  }
22174
22526
  const sourceEventTypes = [...new Set(signalEvents.map((event) => event.event_type))].sort();
22175
- const severity = signalEvents.map((event) => inferSeverity2(event.event_type)).sort((left, right) => severityRank(right) - severityRank(left))[0] ?? "low";
22176
- const latestNormalizedEvent = normalizeEvent(latestSignalEvent);
22527
+ const severity = signalEvents.map((event) => inferSeverity2(event, capturePreset, aggregate.kind)).sort((left, right) => severityRank(right) - severityRank(left))[0] ?? aggregate.severity;
22177
22528
  const generationNumber = signalEvents.length;
22178
22529
  const bundlePath = `${LOCAL_BUNDLE_DIRECTORY_PATH3}/${incidentId}.bundle.json`;
22179
22530
  const reproductionPath = `${LOCAL_REPRODUCTION_DIRECTORY_PATH2}/${incidentId}.reproduction.json`;
@@ -22188,7 +22539,7 @@ async function processCommand(input, dependencies = {}) {
22188
22539
  environment: aggregate.environment,
22189
22540
  fingerprint: aggregate.fingerprint,
22190
22541
  fingerprint_version: FINGERPRINT_VERSION,
22191
- title: latestNormalizedEvent.normalized_message,
22542
+ title: aggregate.title,
22192
22543
  severity,
22193
22544
  status: existingIncidents.some((incidentState) => incidentState.status === "resolved") ? "open" : existing?.status ?? "open",
22194
22545
  first_seen_at: firstSignalEvent.occurred_at,
@@ -22249,21 +22600,22 @@ async function processCommand(input, dependencies = {}) {
22249
22600
  incidents.set(incidentId, incident);
22250
22601
  services.set(incident.service_name, (services.get(incident.service_name) ?? 0) + 1);
22251
22602
  }
22603
+ const finalProcessedEventFile = targetEventFileNames[targetEventFileNames.length - 1];
22252
22604
  const nextState = {
22253
22605
  version: 1,
22254
- last_processed_event_file: newEventFileNames.at(-1) ?? previousState?.last_processed_event_file ?? null,
22606
+ last_processed_event_file: finalProcessedEventFile,
22255
22607
  incidents: Object.fromEntries([...incidents.entries()].sort(([left], [right]) => left.localeCompare(right)))
22256
22608
  };
22257
22609
  await writeFile(statePath, serializeState(nextState));
22258
22610
  const summary = buildProcessedSummary({
22259
22611
  filesProcessed: newEventFileNames.length,
22260
22612
  eventsProcessed,
22261
- incidentsProcessed: mergedAggregates.length,
22613
+ incidentsProcessed: finalizedAggregates.length,
22262
22614
  services: [...services.entries()].sort(([left], [right]) => left.localeCompare(right)).map(([service, count]) => ({
22263
22615
  service,
22264
22616
  incidents: count
22265
22617
  })),
22266
- lastProcessedEventFile: nextState.last_processed_event_file
22618
+ lastProcessedEventFile: finalProcessedEventFile
22267
22619
  });
22268
22620
  return {
22269
22621
  exitCode: 0,
@@ -24248,14 +24600,14 @@ function localFailureStepName(checks) {
24248
24600
  function cloudVerificationRunId(now) {
24249
24601
  return now.toISOString().replace(/[-:.TZ]/g, "").slice(0, 14);
24250
24602
  }
24251
- function requestFailure5xxReason() {
24603
+ function requestFailureReason() {
24252
24604
  const incidentReason = deriveIncidentReasonFromSignal({
24253
24605
  event_type: "request_event",
24254
24606
  event_class: "incident_signal",
24255
24607
  response_status: 503
24256
24608
  });
24257
24609
  if (incidentReason === null) {
24258
- throw new Error("request_failure_5xx_reason_unavailable");
24610
+ throw new Error("request_failure_reason_unavailable");
24259
24611
  }
24260
24612
  return incidentReason;
24261
24613
  }
@@ -24530,7 +24882,7 @@ async function verifyCloudCommand(input, dependencies = {}) {
24530
24882
  const verification = {
24531
24883
  mode: "active_5xx",
24532
24884
  bundle_status: "unknown",
24533
- classification_reason: requestFailure5xxReason()
24885
+ classification_reason: requestFailureReason()
24534
24886
  };
24535
24887
  const errors = [];
24536
24888
  let exitCode = 0;
@@ -24582,7 +24934,7 @@ async function verifyCloudCommand(input, dependencies = {}) {
24582
24934
  if (candidate !== void 0) {
24583
24935
  incidentId = candidate.incident_id;
24584
24936
  verification.incident_id = candidate.incident_id;
24585
- verification.classification_reason = candidate.incident_reason ?? requestFailure5xxReason();
24937
+ verification.classification_reason = candidate.incident_reason ?? requestFailureReason();
24586
24938
  break;
24587
24939
  }
24588
24940
  if (attempt < pollAttempts) {
@@ -24857,7 +25209,7 @@ var CLI_USAGE_LINES = [
24857
25209
  " debugbundle connect [--auth-file <path>] [--json]",
24858
25210
  " debugbundle ingest <file> --format <debugbundle-ndjson|php-error|apache-error> [--json]",
24859
25211
  " debugbundle watch [--cloud] --log <file> --format <debugbundle-ndjson|php-error|apache-error> [--json]",
24860
- " debugbundle process [--json]",
25212
+ " debugbundle process [--preset <minimal|balanced|investigative>] [--json]",
24861
25213
  " debugbundle clean [--events] [--bundles] [--all] [--older-than <Nd>] [--json]",
24862
25214
  " debugbundle validate [--fix] [--json]",
24863
25215
  " debugbundle verify local [--json]",
@@ -24907,6 +25259,10 @@ var CLI_USAGE_LINES = [
24907
25259
  " debugbundle alert create --project-id <id> --channel <channel> --condition <condition> [--service-id <id>] [--severity-min <level>] --config-json <json> [--is-enabled <true|false>] [--auth-file <path>] [--json]",
24908
25260
  " debugbundle alert update <alert-id> [--service-id <id|null>] [--channel <channel>] [--condition <condition>] [--severity-min <level|null>] [--config-json <json|null>] [--is-enabled <true|false>] [--auth-file <path>] [--json]",
24909
25261
  " debugbundle alert delete <alert-id> [--auth-file <path>] [--json]",
25262
+ " debugbundle slack list --project-id <id> [--auth-file <path>] [--json]",
25263
+ " debugbundle slack connect-url --project-id <id> [--return-to </projects/...>] [--auth-file <path>] [--json]",
25264
+ " debugbundle slack test <destination-id> --project-id <id> [--auth-file <path>] [--json]",
25265
+ " debugbundle slack delete <destination-id> --project-id <id> [--auth-file <path>] [--json]",
24910
25266
  " debugbundle webhook list --project-id <id> [--limit <n>] [--auth-file <path>] [--json]",
24911
25267
  " debugbundle webhook create --project-id <id> --url <url> --event <event[,event]> [--environment <env[,env]>] [--service <svc[,svc]>] [--severity-min <level>] [--bundle-type <type[,type]>] [--verification <true|false>] [--is-enabled <true|false>] [--auth-file <path>] [--json]",
24912
25268
  " debugbundle webhook update <webhook-id> [--url <url>] [--event <event[,event]>] [--environment <env[,env]>] [--service <svc[,svc]>] [--severity-min <level>] [--bundle-type <type[,type]>] [--verification <true|false>] [--is-enabled <true|false>] [--auth-file <path>] [--json]",
@@ -26684,8 +27040,153 @@ async function deleteWeeklyReportChannelWithAuthCommand(input, dependencies) {
26684
27040
  });
26685
27041
  }
26686
27042
 
26687
- // src/billing-commands.ts
27043
+ // src/slack-commands.ts
26688
27044
  function mapErrorToExitCode10(error) {
27045
+ if (!(error instanceof SlackApiError)) {
27046
+ return 1;
27047
+ }
27048
+ if (error.status === 401) {
27049
+ return 2;
27050
+ }
27051
+ if (error.status === 404) {
27052
+ return 3;
27053
+ }
27054
+ if (error.status === 400 || error.status === 403) {
27055
+ return 4;
27056
+ }
27057
+ return 1;
27058
+ }
27059
+ function formatSlackDestinationTable(destinations) {
27060
+ if (destinations.length === 0) {
27061
+ return "No Slack destinations found.";
27062
+ }
27063
+ return destinations.map((destination) => {
27064
+ const workspace = destination.slack_team_name ?? destination.slack_team_id;
27065
+ const channel = destination.slack_channel_name ?? destination.slack_channel_id;
27066
+ return `${destination.slack_destination_id} | ${destination.is_active ? "active" : "inactive"} | ${workspace} | ${channel}`;
27067
+ }).join("\n");
27068
+ }
27069
+ async function listSlackDestinationsCommand(input, api) {
27070
+ try {
27071
+ const destinations = await api.listSlackDestinations({
27072
+ bearerToken: input.bearerToken,
27073
+ projectId: input.projectId
27074
+ });
27075
+ return {
27076
+ exitCode: 0,
27077
+ output: input.json ? JSON.stringify({ destinations }) : formatSlackDestinationTable(destinations)
27078
+ };
27079
+ } catch (error) {
27080
+ return { exitCode: mapErrorToExitCode10(error), output: error instanceof Error ? error.message : String(error) };
27081
+ }
27082
+ }
27083
+ async function listSlackDestinationsWithAuthCommand(input, dependencies) {
27084
+ return runAuthenticatedCliCommand(input, {
27085
+ createApi: createAuthenticatedSlackApi,
27086
+ dependencies,
27087
+ runCommand: (authState, api) => listSlackDestinationsCommand(
27088
+ {
27089
+ bearerToken: authState.bearer_token,
27090
+ projectId: input.projectId,
27091
+ ...input.json !== void 0 ? { json: input.json } : {}
27092
+ },
27093
+ api
27094
+ )
27095
+ });
27096
+ }
27097
+ async function getSlackConnectUrlCommand(input, api) {
27098
+ try {
27099
+ const installUrl = await api.getSlackInstallUrl({
27100
+ bearerToken: input.bearerToken,
27101
+ projectId: input.projectId,
27102
+ ...input.returnTo !== void 0 ? { returnTo: input.returnTo } : {}
27103
+ });
27104
+ return {
27105
+ exitCode: 0,
27106
+ output: input.json ? JSON.stringify({ install_url: installUrl }) : installUrl
27107
+ };
27108
+ } catch (error) {
27109
+ return { exitCode: mapErrorToExitCode10(error), output: error instanceof Error ? error.message : String(error) };
27110
+ }
27111
+ }
27112
+ async function getSlackConnectUrlWithAuthCommand(input, dependencies) {
27113
+ return runAuthenticatedCliCommand(input, {
27114
+ createApi: createAuthenticatedSlackApi,
27115
+ dependencies,
27116
+ runCommand: (authState, api) => getSlackConnectUrlCommand(
27117
+ {
27118
+ bearerToken: authState.bearer_token,
27119
+ projectId: input.projectId,
27120
+ ...input.returnTo !== void 0 ? { returnTo: input.returnTo } : {},
27121
+ ...input.json !== void 0 ? { json: input.json } : {}
27122
+ },
27123
+ api
27124
+ )
27125
+ });
27126
+ }
27127
+ async function testSlackDestinationCommand(input, api) {
27128
+ try {
27129
+ const delivery = await api.testSlackDestination({
27130
+ bearerToken: input.bearerToken,
27131
+ projectId: input.projectId,
27132
+ destinationId: input.destinationId
27133
+ });
27134
+ return {
27135
+ exitCode: 0,
27136
+ output: input.json ? JSON.stringify({ delivery }) : `Slack test message delivered for destination: ${input.destinationId}`
27137
+ };
27138
+ } catch (error) {
27139
+ return { exitCode: mapErrorToExitCode10(error), output: error instanceof Error ? error.message : String(error) };
27140
+ }
27141
+ }
27142
+ async function testSlackDestinationWithAuthCommand(input, dependencies) {
27143
+ return runAuthenticatedCliCommand(input, {
27144
+ createApi: createAuthenticatedSlackApi,
27145
+ dependencies,
27146
+ runCommand: (authState, api) => testSlackDestinationCommand(
27147
+ {
27148
+ bearerToken: authState.bearer_token,
27149
+ projectId: input.projectId,
27150
+ destinationId: input.destinationId,
27151
+ ...input.json !== void 0 ? { json: input.json } : {}
27152
+ },
27153
+ api
27154
+ )
27155
+ });
27156
+ }
27157
+ async function deleteSlackDestinationCommand(input, api) {
27158
+ try {
27159
+ const deleted = await api.deleteSlackDestination({
27160
+ bearerToken: input.bearerToken,
27161
+ projectId: input.projectId,
27162
+ destinationId: input.destinationId
27163
+ });
27164
+ return {
27165
+ exitCode: 0,
27166
+ output: input.json ? JSON.stringify({ destination: deleted }) : `Slack destination deleted: ${deleted.slack_destination_id}`
27167
+ };
27168
+ } catch (error) {
27169
+ return { exitCode: mapErrorToExitCode10(error), output: error instanceof Error ? error.message : String(error) };
27170
+ }
27171
+ }
27172
+ async function deleteSlackDestinationWithAuthCommand(input, dependencies) {
27173
+ return runAuthenticatedCliCommand(input, {
27174
+ createApi: createAuthenticatedSlackApi,
27175
+ dependencies,
27176
+ runCommand: (authState, api) => deleteSlackDestinationCommand(
27177
+ {
27178
+ bearerToken: authState.bearer_token,
27179
+ projectId: input.projectId,
27180
+ destinationId: input.destinationId,
27181
+ ...input.json !== void 0 ? { json: input.json } : {}
27182
+ },
27183
+ api
27184
+ )
27185
+ });
27186
+ }
27187
+
27188
+ // src/billing-commands.ts
27189
+ function mapErrorToExitCode11(error) {
26689
27190
  if (!(error instanceof BillingApiError)) {
26690
27191
  return 1;
26691
27192
  }
@@ -26733,7 +27234,7 @@ async function getBillingSummaryCommand(input, api) {
26733
27234
  output: input.json ? JSON.stringify({ billing }) : formatBillingSummary(billing)
26734
27235
  };
26735
27236
  } catch (error) {
26736
- return { exitCode: mapErrorToExitCode10(error), output: error instanceof Error ? error.message : String(error) };
27237
+ return { exitCode: mapErrorToExitCode11(error), output: error instanceof Error ? error.message : String(error) };
26737
27238
  }
26738
27239
  }
26739
27240
  async function increaseBillingCapacityCommand(input, api) {
@@ -26748,7 +27249,7 @@ async function increaseBillingCapacityCommand(input, api) {
26748
27249
  ${formatBillingSummary(billing)}`
26749
27250
  };
26750
27251
  } catch (error) {
26751
- return { exitCode: mapErrorToExitCode10(error), output: error instanceof Error ? error.message : String(error) };
27252
+ return { exitCode: mapErrorToExitCode11(error), output: error instanceof Error ? error.message : String(error) };
26752
27253
  }
26753
27254
  }
26754
27255
  async function scheduleBillingCapacityReductionCommand(input, api) {
@@ -26763,7 +27264,7 @@ async function scheduleBillingCapacityReductionCommand(input, api) {
26763
27264
  ${formatBillingSummary(billing)}`
26764
27265
  };
26765
27266
  } catch (error) {
26766
- return { exitCode: mapErrorToExitCode10(error), output: error instanceof Error ? error.message : String(error) };
27267
+ return { exitCode: mapErrorToExitCode11(error), output: error instanceof Error ? error.message : String(error) };
26767
27268
  }
26768
27269
  }
26769
27270
  async function cancelBillingCapacityReductionCommand(input, api) {
@@ -26775,7 +27276,7 @@ async function cancelBillingCapacityReductionCommand(input, api) {
26775
27276
  ${formatBillingSummary(billing)}`
26776
27277
  };
26777
27278
  } catch (error) {
26778
- return { exitCode: mapErrorToExitCode10(error), output: error instanceof Error ? error.message : String(error) };
27279
+ return { exitCode: mapErrorToExitCode11(error), output: error instanceof Error ? error.message : String(error) };
26779
27280
  }
26780
27281
  }
26781
27282
  async function getBillingSummaryWithAuthCommand(input, dependencies) {
@@ -26936,7 +27437,7 @@ function formatInvitesTable(invites) {
26936
27437
  }
26937
27438
  return invites.map((i) => `${i.invite_id} | ${i.email} | ${i.role} | expires=${i.expires_at}`).join("\n");
26938
27439
  }
26939
- function mapErrorToExitCode11(error) {
27440
+ function mapErrorToExitCode12(error) {
26940
27441
  if (!(error instanceof MemberApiError)) {
26941
27442
  return 1;
26942
27443
  }
@@ -26963,7 +27464,7 @@ async function listMembersCommand(input, api) {
26963
27464
  };
26964
27465
  } catch (error) {
26965
27466
  return {
26966
- exitCode: mapErrorToExitCode11(error),
27467
+ exitCode: mapErrorToExitCode12(error),
26967
27468
  output: error instanceof MemberApiError ? error.code : String(error)
26968
27469
  };
26969
27470
  }
@@ -26977,7 +27478,7 @@ async function listInvitesCommand(input, api) {
26977
27478
  };
26978
27479
  } catch (error) {
26979
27480
  return {
26980
- exitCode: mapErrorToExitCode11(error),
27481
+ exitCode: mapErrorToExitCode12(error),
26981
27482
  output: error instanceof MemberApiError ? error.code : String(error)
26982
27483
  };
26983
27484
  }
@@ -26991,7 +27492,7 @@ async function inviteMemberCommand(input, api) {
26991
27492
  };
26992
27493
  } catch (error) {
26993
27494
  return {
26994
- exitCode: mapErrorToExitCode11(error),
27495
+ exitCode: mapErrorToExitCode12(error),
26995
27496
  output: error instanceof MemberApiError ? error.code : String(error)
26996
27497
  };
26997
27498
  }
@@ -27005,7 +27506,7 @@ async function cancelInviteCommand(input, api) {
27005
27506
  };
27006
27507
  } catch (error) {
27007
27508
  return {
27008
- exitCode: mapErrorToExitCode11(error),
27509
+ exitCode: mapErrorToExitCode12(error),
27009
27510
  output: error instanceof MemberApiError ? error.code : String(error)
27010
27511
  };
27011
27512
  }
@@ -27019,7 +27520,7 @@ async function updateMemberRoleCommand(input, api) {
27019
27520
  };
27020
27521
  } catch (error) {
27021
27522
  return {
27022
- exitCode: mapErrorToExitCode11(error),
27523
+ exitCode: mapErrorToExitCode12(error),
27023
27524
  output: error instanceof MemberApiError ? error.code : String(error)
27024
27525
  };
27025
27526
  }
@@ -27033,7 +27534,7 @@ async function removeMemberCommand(input, api) {
27033
27534
  };
27034
27535
  } catch (error) {
27035
27536
  return {
27036
- exitCode: mapErrorToExitCode11(error),
27537
+ exitCode: mapErrorToExitCode12(error),
27037
27538
  output: error instanceof MemberApiError ? error.code : String(error)
27038
27539
  };
27039
27540
  }
@@ -27182,7 +27683,7 @@ function createProbeApi(httpClient) {
27182
27683
  }
27183
27684
  };
27184
27685
  }
27185
- function mapErrorToExitCode12(error) {
27686
+ function mapErrorToExitCode13(error) {
27186
27687
  if (!(error instanceof ProbeApiError)) {
27187
27688
  return 1;
27188
27689
  }
@@ -27235,7 +27736,7 @@ async function activateProbeCommand(input, api) {
27235
27736
  Trigger token: ${result.trigger_token}`
27236
27737
  };
27237
27738
  } catch (error) {
27238
- return { exitCode: mapErrorToExitCode12(error), output: error instanceof Error ? error.message : String(error) };
27739
+ return { exitCode: mapErrorToExitCode13(error), output: error instanceof Error ? error.message : String(error) };
27239
27740
  }
27240
27741
  }
27241
27742
  async function listActiveProbesCommand(input, api) {
@@ -27255,7 +27756,7 @@ async function listActiveProbesCommand(input, api) {
27255
27756
  output: result.activations.map((a) => `${a.activation_id} ${a.label_pattern} (${a.service}/${a.environment}) expires ${a.expires_at}`).join("\n")
27256
27757
  };
27257
27758
  } catch (error) {
27258
- return { exitCode: mapErrorToExitCode12(error), output: error instanceof Error ? error.message : String(error) };
27759
+ return { exitCode: mapErrorToExitCode13(error), output: error instanceof Error ? error.message : String(error) };
27259
27760
  }
27260
27761
  }
27261
27762
  async function deactivateProbeCommand(input, api) {
@@ -27273,7 +27774,7 @@ async function deactivateProbeCommand(input, api) {
27273
27774
  output: result.deactivated ? "Probe deactivated." : "Probe was already inactive."
27274
27775
  };
27275
27776
  } catch (error) {
27276
- return { exitCode: mapErrorToExitCode12(error), output: error instanceof Error ? error.message : String(error) };
27777
+ return { exitCode: mapErrorToExitCode13(error), output: error instanceof Error ? error.message : String(error) };
27277
27778
  }
27278
27779
  }
27279
27780
  async function createAuthenticatedProbeApi(input, dependencies) {
@@ -27323,7 +27824,7 @@ async function deactivateProbeWithAuthCommand(input, dependencies) {
27323
27824
  }
27324
27825
 
27325
27826
  // src/github-commands.ts
27326
- function mapErrorToExitCode13(error) {
27827
+ function mapErrorToExitCode14(error) {
27327
27828
  if (!(error instanceof GitHubManagementApiError)) {
27328
27829
  return 1;
27329
27830
  }
@@ -27384,7 +27885,7 @@ async function getGitHubStatusCommand(input, api) {
27384
27885
  ${formatProjectRepo(repo)}`
27385
27886
  };
27386
27887
  } catch (error) {
27387
- return { exitCode: mapErrorToExitCode13(error), output: error instanceof Error ? error.message : String(error) };
27888
+ return { exitCode: mapErrorToExitCode14(error), output: error instanceof Error ? error.message : String(error) };
27388
27889
  }
27389
27890
  }
27390
27891
  async function listGitHubRepositoriesCommand(input, api) {
@@ -27395,7 +27896,7 @@ async function listGitHubRepositoriesCommand(input, api) {
27395
27896
  output: input.json ? JSON.stringify({ repositories }) : repositories.length === 0 ? "No GitHub repositories found." : repositories.map((repository) => `${repository.full_name} (${repository.default_branch})`).join("\n")
27396
27897
  };
27397
27898
  } catch (error) {
27398
- return { exitCode: mapErrorToExitCode13(error), output: error instanceof Error ? error.message : String(error) };
27899
+ return { exitCode: mapErrorToExitCode14(error), output: error instanceof Error ? error.message : String(error) };
27399
27900
  }
27400
27901
  }
27401
27902
  async function setProjectGitHubRepoCommand(input, api) {
@@ -27418,7 +27919,7 @@ async function setProjectGitHubRepoCommand(input, api) {
27418
27919
  output: input.json ? JSON.stringify({ repo: assignedRepo }) : `Project repo set: ${formatProjectRepo(assignedRepo)}`
27419
27920
  };
27420
27921
  } catch (error) {
27421
- return { exitCode: mapErrorToExitCode13(error), output: error instanceof Error ? error.message : String(error) };
27922
+ return { exitCode: mapErrorToExitCode14(error), output: error instanceof Error ? error.message : String(error) };
27422
27923
  }
27423
27924
  }
27424
27925
  async function removeProjectGitHubRepoCommand(input, api) {
@@ -27429,7 +27930,7 @@ async function removeProjectGitHubRepoCommand(input, api) {
27429
27930
  output: input.json ? JSON.stringify({ removed: true, project_id: input.projectId }) : `Project repo removed: ${input.projectId}`
27430
27931
  };
27431
27932
  } catch (error) {
27432
- return { exitCode: mapErrorToExitCode13(error), output: error instanceof Error ? error.message : String(error) };
27933
+ return { exitCode: mapErrorToExitCode14(error), output: error instanceof Error ? error.message : String(error) };
27433
27934
  }
27434
27935
  }
27435
27936
  async function listProjectGitHubRulesCommand(input, api) {
@@ -27443,7 +27944,7 @@ async function listProjectGitHubRulesCommand(input, api) {
27443
27944
  output: input.json ? JSON.stringify({ rules }) : formatGitHubRuleTable(rules)
27444
27945
  };
27445
27946
  } catch (error) {
27446
- return { exitCode: mapErrorToExitCode13(error), output: error instanceof Error ? error.message : String(error) };
27947
+ return { exitCode: mapErrorToExitCode14(error), output: error instanceof Error ? error.message : String(error) };
27447
27948
  }
27448
27949
  }
27449
27950
  async function listProjectGitHubDeliveriesCommand(input, api) {
@@ -27459,7 +27960,7 @@ async function listProjectGitHubDeliveriesCommand(input, api) {
27459
27960
  output: input.json ? JSON.stringify({ deliveries }) : formatGitHubDeliveryTable(deliveries)
27460
27961
  };
27461
27962
  } catch (error) {
27462
- return { exitCode: mapErrorToExitCode13(error), output: error instanceof Error ? error.message : String(error) };
27963
+ return { exitCode: mapErrorToExitCode14(error), output: error instanceof Error ? error.message : String(error) };
27463
27964
  }
27464
27965
  }
27465
27966
  async function retryProjectGitHubDeliveryCommand(input, api) {
@@ -27474,7 +27975,7 @@ async function retryProjectGitHubDeliveryCommand(input, api) {
27474
27975
  output: input.json ? JSON.stringify({ delivery }) : `GitHub delivery retried: ${delivery.delivery_id} | ${delivery.status}`
27475
27976
  };
27476
27977
  } catch (error) {
27477
- return { exitCode: mapErrorToExitCode13(error), output: error instanceof Error ? error.message : String(error) };
27978
+ return { exitCode: mapErrorToExitCode14(error), output: error instanceof Error ? error.message : String(error) };
27478
27979
  }
27479
27980
  }
27480
27981
  async function createProjectGitHubRuleCommand(input, api) {
@@ -27497,7 +27998,7 @@ async function createProjectGitHubRuleCommand(input, api) {
27497
27998
  output: input.json ? JSON.stringify({ rule }) : `GitHub rule created: ${rule.rule_id}`
27498
27999
  };
27499
28000
  } catch (error) {
27500
- return { exitCode: mapErrorToExitCode13(error), output: error instanceof Error ? error.message : String(error) };
28001
+ return { exitCode: mapErrorToExitCode14(error), output: error instanceof Error ? error.message : String(error) };
27501
28002
  }
27502
28003
  }
27503
28004
  async function updateProjectGitHubRuleCommand(input, api) {
@@ -27521,7 +28022,7 @@ async function updateProjectGitHubRuleCommand(input, api) {
27521
28022
  output: input.json ? JSON.stringify({ rule }) : `GitHub rule updated: ${rule.rule_id}`
27522
28023
  };
27523
28024
  } catch (error) {
27524
- return { exitCode: mapErrorToExitCode13(error), output: error instanceof Error ? error.message : String(error) };
28025
+ return { exitCode: mapErrorToExitCode14(error), output: error instanceof Error ? error.message : String(error) };
27525
28026
  }
27526
28027
  }
27527
28028
  async function deleteProjectGitHubRuleCommand(input, api) {
@@ -27536,7 +28037,7 @@ async function deleteProjectGitHubRuleCommand(input, api) {
27536
28037
  output: input.json ? JSON.stringify({ deleted: true, project_id: input.projectId, rule_id: input.ruleId }) : `GitHub rule deleted: ${input.ruleId}`
27537
28038
  };
27538
28039
  } catch (error) {
27539
- return { exitCode: mapErrorToExitCode13(error), output: error instanceof Error ? error.message : String(error) };
28040
+ return { exitCode: mapErrorToExitCode14(error), output: error instanceof Error ? error.message : String(error) };
27540
28041
  }
27541
28042
  }
27542
28043
  async function getGitHubStatusWithAuthCommand(input, dependencies) {
@@ -28534,6 +29035,67 @@ async function handleAlertCommand(parsedArgv, dependencies) {
28534
29035
  }
28535
29036
  throw new CliInputError("Unknown alert command.");
28536
29037
  }
29038
+ async function handleSlackCommand(parsedArgv, dependencies) {
29039
+ const action = requirePositional(parsedArgv, 1, "action");
29040
+ if (action === "list") {
29041
+ expectNoUnknownOptions(parsedArgv, ["auth-file", "json", "project-id"]);
29042
+ ensureNoExtraPositionals(parsedArgv, 2);
29043
+ const projectId = readStringOption(parsedArgv, "project-id");
29044
+ if (projectId === void 0) {
29045
+ throw new CliInputError("Missing required option --project-id.");
29046
+ }
29047
+ return await (dependencies.listSlackDestinationsCommand ?? listSlackDestinationsWithAuthCommand)(
29048
+ appendCommonAuthOptions(parsedArgv, {
29049
+ projectId
29050
+ })
29051
+ );
29052
+ }
29053
+ if (action === "connect-url") {
29054
+ expectNoUnknownOptions(parsedArgv, ["auth-file", "json", "project-id", "return-to"]);
29055
+ ensureNoExtraPositionals(parsedArgv, 2);
29056
+ const projectId = readStringOption(parsedArgv, "project-id");
29057
+ if (projectId === void 0) {
29058
+ throw new CliInputError("Missing required option --project-id.");
29059
+ }
29060
+ const input = appendCommonAuthOptions(parsedArgv, {
29061
+ projectId
29062
+ });
29063
+ const returnTo = readStringOption(parsedArgv, "return-to");
29064
+ if (returnTo !== void 0) {
29065
+ input.returnTo = returnTo;
29066
+ }
29067
+ return await (dependencies.getSlackConnectUrlCommand ?? getSlackConnectUrlWithAuthCommand)(input);
29068
+ }
29069
+ if (action === "test") {
29070
+ expectNoUnknownOptions(parsedArgv, ["auth-file", "json", "project-id"]);
29071
+ ensureNoExtraPositionals(parsedArgv, 3);
29072
+ const projectId = readStringOption(parsedArgv, "project-id");
29073
+ if (projectId === void 0) {
29074
+ throw new CliInputError("Missing required option --project-id.");
29075
+ }
29076
+ return await (dependencies.testSlackDestinationCommand ?? testSlackDestinationWithAuthCommand)(
29077
+ appendCommonAuthOptions(parsedArgv, {
29078
+ projectId,
29079
+ destinationId: requirePositional(parsedArgv, 2, "destination-id")
29080
+ })
29081
+ );
29082
+ }
29083
+ if (action === "delete") {
29084
+ expectNoUnknownOptions(parsedArgv, ["auth-file", "json", "project-id"]);
29085
+ ensureNoExtraPositionals(parsedArgv, 3);
29086
+ const projectId = readStringOption(parsedArgv, "project-id");
29087
+ if (projectId === void 0) {
29088
+ throw new CliInputError("Missing required option --project-id.");
29089
+ }
29090
+ return await (dependencies.deleteSlackDestinationCommand ?? deleteSlackDestinationWithAuthCommand)(
29091
+ appendCommonAuthOptions(parsedArgv, {
29092
+ projectId,
29093
+ destinationId: requirePositional(parsedArgv, 2, "destination-id")
29094
+ })
29095
+ );
29096
+ }
29097
+ throw new CliInputError("Unknown slack command.");
29098
+ }
28537
29099
  async function handleWeeklyReportCommand(parsedArgv, dependencies) {
28538
29100
  const action = requirePositional(parsedArgv, 1, "action");
28539
29101
  if (action === "list") {
@@ -28589,10 +29151,20 @@ async function handleWeeklyReportCommand(parsedArgv, dependencies) {
28589
29151
  if (config === void 0 || typeof config !== "object" || config === null) {
28590
29152
  throw new CliInputError("Missing required option --config-json.");
28591
29153
  }
29154
+ let weeklyReportConfig;
29155
+ if (channel === "slack") {
29156
+ if (typeof config["slack_destination_id"] === "string") {
29157
+ weeklyReportConfig = { slackDestinationId: String(config["slack_destination_id"]) };
29158
+ } else {
29159
+ weeklyReportConfig = { webhookUrl: String(config["webhook_url"]) };
29160
+ }
29161
+ } else {
29162
+ weeklyReportConfig = { to: config.to };
29163
+ }
28592
29164
  const input = appendCommonAuthOptions(parsedArgv, {
28593
29165
  projectId,
28594
29166
  channel,
28595
- config: channel === "slack" ? { webhookUrl: String(config["webhook_url"]) } : { to: config.to },
29167
+ config: weeklyReportConfig,
28596
29168
  schedule: {
28597
29169
  dayOfWeek,
28598
29170
  hourOfDay,
@@ -28633,7 +29205,7 @@ async function handleWeeklyReportCommand(parsedArgv, dependencies) {
28633
29205
  if (typeof config !== "object" || config === null) {
28634
29206
  throw new CliInputError("Invalid value for --config-json.");
28635
29207
  }
28636
- input.config = "webhook_url" in config ? { webhookUrl: String(config["webhook_url"]) } : { to: config.to };
29208
+ input.config = "slack_destination_id" in config ? { slackDestinationId: String(config["slack_destination_id"]) } : "webhook_url" in config ? { webhookUrl: String(config["webhook_url"]) } : { to: config.to };
28637
29209
  }
28638
29210
  const isEnabled = readBooleanStringOption(parsedArgv, "is-enabled");
28639
29211
  if (isEnabled !== void 0) {
@@ -28761,10 +29333,17 @@ ${formatUsage()}`
28761
29333
  });
28762
29334
  }
28763
29335
  if (command === "process") {
28764
- expectNoUnknownOptions(parsedArgv, ["json"]);
29336
+ expectNoUnknownOptions(parsedArgv, ["json", "preset"]);
28765
29337
  ensureNoExtraPositionals(parsedArgv, 1);
28766
29338
  const json = readBooleanOption(parsedArgv, "json");
28767
- return await (dependencies.processCommand ?? processCommand)(json === true ? { json: true } : {});
29339
+ const preset = readStringOption(parsedArgv, "preset");
29340
+ if (preset !== void 0 && !CapturePresetSchema.safeParse(preset).success) {
29341
+ throw new CliInputError("Invalid value for --preset.");
29342
+ }
29343
+ return await (dependencies.processCommand ?? processCommand)({
29344
+ ...json === true ? { json: true } : {},
29345
+ ...preset !== void 0 ? { preset } : {}
29346
+ });
28768
29347
  }
28769
29348
  if (command === "clean") {
28770
29349
  expectNoUnknownOptions(parsedArgv, ["events", "bundles", "all", "older-than", "json"]);
@@ -29041,6 +29620,9 @@ ${formatUsage()}`
29041
29620
  if (command === "alert") {
29042
29621
  return await handleAlertCommand(parsedArgv, dependencies);
29043
29622
  }
29623
+ if (command === "slack") {
29624
+ return await handleSlackCommand(parsedArgv, dependencies);
29625
+ }
29044
29626
  if (command === "webhook") {
29045
29627
  return await handleWebhookCommand(parsedArgv, dependencies);
29046
29628
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@debugbundle/cli",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "private": false,
5
5
  "description": "Command-line interface for DebugBundle",
6
6
  "license": "AGPL-3.0-only",