@debugbundle/cli 0.1.3 → 0.1.5

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 +575 -58
  2. package/package.json +1 -1
package/dist/main.cjs CHANGED
@@ -14485,6 +14485,31 @@ var CaptureProbeEventsValues = ["buffer_only", "standalone_when_activated"];
14485
14485
  var CaptureProbeEventsSchema = external_exports.enum(CaptureProbeEventsValues);
14486
14486
  var RequestSignalClassificationValues = ["incident_signal", "context_signal"];
14487
14487
  var RequestSignalClassificationSchema = external_exports.enum(RequestSignalClassificationValues);
14488
+ var RECOMMENDED_IMMEDIATE_CLIENT_ERROR_STATUSES = [401, 403, 409, 422];
14489
+ var ImmediateClientErrorStatusSchema = external_exports.number().int().min(400).max(499);
14490
+ function normalizeImmediateClientErrorStatuses(statuses) {
14491
+ return Array.from(new Set(statuses)).sort((left, right) => left - right);
14492
+ }
14493
+ var ImmediateClientErrorStatusesSchema = external_exports.array(ImmediateClientErrorStatusSchema).max(12).transform((statuses) => normalizeImmediateClientErrorStatuses(statuses));
14494
+ var ResolvedCapturePolicySchema = external_exports.object({
14495
+ preset: CapturePresetSchema,
14496
+ capture_logs: CaptureLogsSchema,
14497
+ capture_request_events: CaptureRequestEventsSchema,
14498
+ capture_breadcrumbs: CaptureBreadcrumbsSchema,
14499
+ capture_probe_events: CaptureProbeEventsSchema,
14500
+ immediate_client_error_statuses: ImmediateClientErrorStatusesSchema
14501
+ });
14502
+ var CapturePolicyOverridesSchema = external_exports.object({
14503
+ capture_logs: CaptureLogsSchema.nullable(),
14504
+ capture_request_events: CaptureRequestEventsSchema.nullable(),
14505
+ capture_breadcrumbs: CaptureBreadcrumbsSchema.nullable(),
14506
+ capture_probe_events: CaptureProbeEventsSchema.nullable(),
14507
+ immediate_client_error_statuses: ImmediateClientErrorStatusesSchema.nullable()
14508
+ });
14509
+ var CapturePolicyResponseSchema = external_exports.object({
14510
+ policy: ResolvedCapturePolicySchema,
14511
+ overrides: CapturePolicyOverridesSchema
14512
+ });
14488
14513
  var CapturePolicySchema = external_exports.object({
14489
14514
  project_id: external_exports.string().uuid(),
14490
14515
  preset: CapturePresetSchema,
@@ -14492,6 +14517,7 @@ var CapturePolicySchema = external_exports.object({
14492
14517
  capture_request_events: CaptureRequestEventsSchema.nullable(),
14493
14518
  capture_breadcrumbs: CaptureBreadcrumbsSchema.nullable(),
14494
14519
  capture_probe_events: CaptureProbeEventsSchema.nullable(),
14520
+ immediate_client_error_statuses: ImmediateClientErrorStatusesSchema.nullable(),
14495
14521
  updated_at: external_exports.string().datetime()
14496
14522
  });
14497
14523
  var CapturePolicyUpdateSchema = external_exports.object({
@@ -14499,21 +14525,48 @@ var CapturePolicyUpdateSchema = external_exports.object({
14499
14525
  capture_logs: CaptureLogsSchema.nullable().optional(),
14500
14526
  capture_request_events: CaptureRequestEventsSchema.nullable().optional(),
14501
14527
  capture_breadcrumbs: CaptureBreadcrumbsSchema.nullable().optional(),
14502
- capture_probe_events: CaptureProbeEventsSchema.nullable().optional()
14528
+ capture_probe_events: CaptureProbeEventsSchema.nullable().optional(),
14529
+ immediate_client_error_statuses: ImmediateClientErrorStatusesSchema.nullable().optional()
14503
14530
  });
14531
+ var PRESET_DEFAULTS = {
14532
+ minimal: {
14533
+ capture_logs: "error",
14534
+ capture_request_events: "failures_only",
14535
+ capture_breadcrumbs: "local_only",
14536
+ capture_probe_events: "buffer_only",
14537
+ immediate_client_error_statuses: []
14538
+ },
14539
+ balanced: {
14540
+ capture_logs: "warning",
14541
+ capture_request_events: "failures_only",
14542
+ capture_breadcrumbs: "exception_only",
14543
+ capture_probe_events: "buffer_only",
14544
+ immediate_client_error_statuses: []
14545
+ },
14546
+ investigative: {
14547
+ capture_logs: "info",
14548
+ capture_request_events: "all",
14549
+ capture_breadcrumbs: "standalone",
14550
+ capture_probe_events: "standalone_when_activated",
14551
+ immediate_client_error_statuses: [...RECOMMENDED_IMMEDIATE_CLIENT_ERROR_STATUSES]
14552
+ }
14553
+ };
14504
14554
  var BALANCED_IMMEDIATE_REQUEST_STATUSES = /* @__PURE__ */ new Set([408, 423, 424, 425, 429]);
14505
14555
  var INVESTIGATIVE_IMMEDIATE_REQUEST_STATUSES = /* @__PURE__ */ new Set([...BALANCED_IMMEDIATE_REQUEST_STATUSES, 409]);
14506
14556
  var BALANCED_STANDARD_ANOMALY_STATUSES = /* @__PURE__ */ new Set([401, 403, 404, 409, 422]);
14507
14557
  var BALANCED_HIGH_VOLUME_ANOMALY_STATUSES = /* @__PURE__ */ new Set([400, 410]);
14508
14558
  var INVESTIGATIVE_ANOMALY_STATUSES = /* @__PURE__ */ new Set([...BALANCED_STANDARD_ANOMALY_STATUSES, ...BALANCED_HIGH_VOLUME_ANOMALY_STATUSES]);
14509
14559
  function classifyRequestStatus(input) {
14510
- const { responseStatus, capturePreset } = input;
14560
+ const { responseStatus, capturePreset, immediateClientErrorStatuses = [] } = input;
14511
14561
  if (responseStatus === null || !Number.isFinite(responseStatus)) {
14512
14562
  return "context_signal";
14513
14563
  }
14514
14564
  if (responseStatus >= 500) {
14515
14565
  return "incident_signal";
14516
14566
  }
14567
+ if (immediateClientErrorStatuses.includes(responseStatus)) {
14568
+ return "incident_signal";
14569
+ }
14517
14570
  if (capturePreset === "investigative") {
14518
14571
  return INVESTIGATIVE_IMMEDIATE_REQUEST_STATUSES.has(responseStatus) ? "incident_signal" : "context_signal";
14519
14572
  }
@@ -16565,7 +16618,7 @@ function getRequestResponseStatus(payload) {
16565
16618
  const status = payload?.["response_status"];
16566
16619
  return typeof status === "number" && Number.isFinite(status) ? status : null;
16567
16620
  }
16568
- function classifyEvent(eventType, logLevel, probeActivationId, payload, capturePreset = "minimal") {
16621
+ function classifyEvent(eventType, logLevel, probeActivationId, payload, capturePreset = "minimal", immediateClientErrorStatuses = []) {
16569
16622
  switch (eventType) {
16570
16623
  case "backend_exception":
16571
16624
  case "frontend_exception":
@@ -16577,7 +16630,7 @@ function classifyEvent(eventType, logLevel, probeActivationId, payload, captureP
16577
16630
  return "context_signal";
16578
16631
  case "request_event": {
16579
16632
  const responseStatus = getRequestResponseStatus(payload);
16580
- return classifyRequestStatus({ responseStatus, capturePreset });
16633
+ return classifyRequestStatus({ responseStatus, capturePreset, immediateClientErrorStatuses });
16581
16634
  }
16582
16635
  case "frontend_breadcrumb":
16583
16636
  case "deploy_metadata":
@@ -16654,6 +16707,39 @@ var STORAGE_SCHEMA_MIGRATIONS = [
16654
16707
  statements: [
16655
16708
  "ALTER TABLE webhook_deliveries ALTER COLUMN incident_id DROP NOT NULL"
16656
16709
  ]
16710
+ }),
16711
+ defineStorageSchemaMigration({
16712
+ id: "202605130002_add_slack_destinations",
16713
+ description: "Add reusable encrypted Slack alert destinations scoped to organizations.",
16714
+ statements: [
16715
+ `
16716
+ CREATE TABLE IF NOT EXISTS slack_destinations (
16717
+ id uuid PRIMARY KEY,
16718
+ organization_id uuid NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
16719
+ slack_team_id text NOT NULL,
16720
+ slack_team_name text,
16721
+ slack_channel_id text NOT NULL,
16722
+ slack_channel_name text,
16723
+ webhook_url_ciphertext text NOT NULL,
16724
+ installed_by_member_id uuid REFERENCES users(id) ON DELETE SET NULL,
16725
+ is_active boolean NOT NULL DEFAULT true,
16726
+ created_at timestamptz NOT NULL DEFAULT now(),
16727
+ updated_at timestamptz NOT NULL DEFAULT now(),
16728
+ UNIQUE (organization_id, slack_team_id, slack_channel_id)
16729
+ )
16730
+ `,
16731
+ `
16732
+ CREATE INDEX IF NOT EXISTS slack_destinations_org_active_idx
16733
+ ON slack_destinations (organization_id, is_active, created_at)
16734
+ `
16735
+ ]
16736
+ }),
16737
+ defineStorageSchemaMigration({
16738
+ id: "202605140001_add_capture_policy_immediate_client_error_statuses",
16739
+ description: "Add nullable immediate client error status overrides to capture policies.",
16740
+ statements: [
16741
+ "ALTER TABLE capture_policies ADD COLUMN IF NOT EXISTS immediate_client_error_statuses jsonb"
16742
+ ]
16657
16743
  })
16658
16744
  ];
16659
16745
 
@@ -18317,7 +18403,7 @@ function createWeeklyReportApi(client) {
18317
18403
  );
18318
18404
  },
18319
18405
  async createWeeklyReportChannel(input) {
18320
- const config = input.channel === "email" ? { to: input.config.to } : { webhook_url: input.config.webhookUrl };
18406
+ const config = input.channel === "email" ? { to: input.config.to } : "webhookUrl" in input.config ? { webhook_url: input.config.webhookUrl } : { slack_destination_id: input.config.slackDestinationId };
18321
18407
  return expectChannel(
18322
18408
  client.request({
18323
18409
  method: "POST",
@@ -18340,7 +18426,7 @@ function createWeeklyReportApi(client) {
18340
18426
  async updateWeeklyReportChannel(input) {
18341
18427
  const body = {};
18342
18428
  if (input.config !== void 0) {
18343
- body["config"] = "to" in input.config ? { to: input.config.to } : { webhook_url: input.config.webhookUrl };
18429
+ 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 };
18344
18430
  }
18345
18431
  if (input.schedule !== void 0) {
18346
18432
  body["schedule"] = {
@@ -18809,6 +18895,121 @@ function createGitHubManagementApi(client) {
18809
18895
  };
18810
18896
  }
18811
18897
 
18898
+ // ../../packages/slack-client/src/index.ts
18899
+ var SlackDestinationRecordSchema = external_exports.object({
18900
+ slack_destination_id: external_exports.string(),
18901
+ organization_id: external_exports.string(),
18902
+ slack_team_id: external_exports.string(),
18903
+ slack_team_name: external_exports.string().nullable(),
18904
+ slack_channel_id: external_exports.string(),
18905
+ slack_channel_name: external_exports.string().nullable(),
18906
+ installed_by_member_id: external_exports.string().nullable(),
18907
+ is_active: external_exports.boolean(),
18908
+ created_at: external_exports.string(),
18909
+ updated_at: external_exports.string()
18910
+ }).strict();
18911
+ var SlackDestinationListResponseSchema = external_exports.object({
18912
+ destinations: external_exports.array(SlackDestinationRecordSchema)
18913
+ }).strict();
18914
+ var SlackInstallUrlResponseSchema = external_exports.object({
18915
+ install_url: external_exports.string().url()
18916
+ }).strict();
18917
+ var SlackDestinationTestResponseSchema = external_exports.object({
18918
+ delivered: external_exports.boolean()
18919
+ }).strict();
18920
+ var ApiErrorResponseSchema9 = external_exports.object({
18921
+ error: external_exports.string()
18922
+ }).strict();
18923
+ var SlackApiError = class extends Error {
18924
+ status;
18925
+ code;
18926
+ constructor(status, code) {
18927
+ super(`slack_api_error: ${status}:${code}`);
18928
+ this.status = status;
18929
+ this.code = code;
18930
+ }
18931
+ };
18932
+ function parseApiError9(status, body) {
18933
+ const parsed = ApiErrorResponseSchema9.safeParse(body);
18934
+ if (!parsed.success) {
18935
+ throw new SlackApiError(status, "unknown_error");
18936
+ }
18937
+ throw new SlackApiError(status, parsed.data.error);
18938
+ }
18939
+ function buildQuery4(input) {
18940
+ const params = new URLSearchParams();
18941
+ for (const [key, value] of Object.entries(input)) {
18942
+ if (value !== void 0) {
18943
+ params.set(key, String(value));
18944
+ }
18945
+ }
18946
+ const query = params.toString();
18947
+ return query.length === 0 ? "" : `?${query}`;
18948
+ }
18949
+ function createSlackApi(client) {
18950
+ return {
18951
+ async getSlackInstallUrl(input) {
18952
+ const response = await client.request({
18953
+ method: "GET",
18954
+ path: `/v1/slack/app/install-url${buildQuery4({
18955
+ project_id: input.projectId,
18956
+ return_to: input.returnTo
18957
+ })}`,
18958
+ bearerToken: input.bearerToken
18959
+ });
18960
+ if (response.status < 200 || response.status >= 300) {
18961
+ parseApiError9(response.status, response.body);
18962
+ }
18963
+ const parsed = SlackInstallUrlResponseSchema.safeParse(response.body);
18964
+ if (!parsed.success) {
18965
+ throw new SlackApiError(response.status, "invalid_response_shape");
18966
+ }
18967
+ return parsed.data.install_url;
18968
+ },
18969
+ async listSlackDestinations(input) {
18970
+ const response = await client.request({
18971
+ method: "GET",
18972
+ path: `/v1/projects/${input.projectId}/slack/destinations`,
18973
+ bearerToken: input.bearerToken
18974
+ });
18975
+ if (response.status < 200 || response.status >= 300) {
18976
+ parseApiError9(response.status, response.body);
18977
+ }
18978
+ const parsed = SlackDestinationListResponseSchema.safeParse(response.body);
18979
+ if (!parsed.success) {
18980
+ throw new SlackApiError(response.status, "invalid_response_shape");
18981
+ }
18982
+ return parsed.data.destinations;
18983
+ },
18984
+ async testSlackDestination(input) {
18985
+ const response = await client.request({
18986
+ method: "POST",
18987
+ path: `/v1/projects/${input.projectId}/slack/destinations/${input.destinationId}/test`,
18988
+ bearerToken: input.bearerToken
18989
+ });
18990
+ if (response.status < 200 || response.status >= 300) {
18991
+ parseApiError9(response.status, response.body);
18992
+ }
18993
+ const parsed = SlackDestinationTestResponseSchema.safeParse(response.body);
18994
+ if (!parsed.success || parsed.data.delivered !== true) {
18995
+ throw new SlackApiError(response.status, "invalid_response_shape");
18996
+ }
18997
+ return { delivered: true };
18998
+ },
18999
+ async deleteSlackDestination(input) {
19000
+ const response = await client.request({
19001
+ method: "DELETE",
19002
+ path: `/v1/projects/${input.projectId}/slack/destinations/${input.destinationId}`,
19003
+ bearerToken: input.bearerToken
19004
+ });
19005
+ if (response.status < 200 || response.status >= 300) {
19006
+ parseApiError9(response.status, response.body);
19007
+ }
19008
+ return { slack_destination_id: input.destinationId };
19009
+ }
19010
+ };
19011
+ }
19012
+
18812
19013
  // src/auth-context.ts
18813
19014
  function normalizeBaseUrl(baseUrl) {
18814
19015
  return baseUrl.endsWith("/") ? baseUrl.slice(0, -1) : baseUrl;
@@ -18984,6 +19185,29 @@ async function createAuthenticatedWeeklyReportApi(input, dependencies) {
18984
19185
  api: createApi(httpClient)
18985
19186
  };
18986
19187
  }
19188
+ async function createAuthenticatedSlackApi(input, dependencies) {
19189
+ const readAuthState = dependencies?.readAuthState ?? readCliAuthState;
19190
+ const authStateInput = {};
19191
+ if (input.authFilePath !== void 0) {
19192
+ authStateInput.authFilePath = input.authFilePath;
19193
+ }
19194
+ const authState = await readAuthState(authStateInput);
19195
+ const createHttpClient = dependencies?.createHttpClient ?? ((clientInput) => {
19196
+ const httpClientDependencies = {};
19197
+ if (dependencies?.fetchImpl !== void 0) {
19198
+ httpClientDependencies.fetchImpl = dependencies.fetchImpl;
19199
+ }
19200
+ return createCliHttpClient(clientInput, httpClientDependencies);
19201
+ });
19202
+ const httpClient = createHttpClient({
19203
+ baseUrl: authState.base_url
19204
+ });
19205
+ const createApi = dependencies?.createApi ?? createSlackApi;
19206
+ return {
19207
+ authState,
19208
+ api: createApi(httpClient)
19209
+ };
19210
+ }
18987
19211
  async function createAuthenticatedProjectManagementApi(input, dependencies) {
18988
19212
  const readAuthState = dependencies?.readAuthState ?? readCliAuthState;
18989
19213
  const authStateInput = {};
@@ -19169,7 +19393,7 @@ var LoginCommandInputSchema = external_exports.object({
19169
19393
  });
19170
19394
  }
19171
19395
  });
19172
- var ApiErrorResponseSchema9 = external_exports.object({
19396
+ var ApiErrorResponseSchema10 = external_exports.object({
19173
19397
  error: external_exports.string()
19174
19398
  }).strict();
19175
19399
  var DeviceStartResponseSchema = external_exports.object({
@@ -19345,7 +19569,7 @@ async function requestJson(input, dependencies) {
19345
19569
  });
19346
19570
  const body = await parseResponseBody2(response);
19347
19571
  if (response.status < 200 || response.status >= 300) {
19348
- const parsedError = ApiErrorResponseSchema9.safeParse(body);
19572
+ const parsedError = ApiErrorResponseSchema10.safeParse(body);
19349
19573
  throw new LoginApiError(response.status, parsedError.success ? parsedError.data.error : "unknown_error");
19350
19574
  }
19351
19575
  return body;
@@ -25095,6 +25319,10 @@ var CLI_USAGE_LINES = [
25095
25319
  " 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]",
25096
25320
  " 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]",
25097
25321
  " debugbundle alert delete <alert-id> [--auth-file <path>] [--json]",
25322
+ " debugbundle slack list --project-id <id> [--auth-file <path>] [--json]",
25323
+ " debugbundle slack connect-url --project-id <id> [--return-to </projects/...>] [--auth-file <path>] [--json]",
25324
+ " debugbundle slack test <destination-id> --project-id <id> [--auth-file <path>] [--json]",
25325
+ " debugbundle slack delete <destination-id> --project-id <id> [--auth-file <path>] [--json]",
25098
25326
  " debugbundle webhook list --project-id <id> [--limit <n>] [--auth-file <path>] [--json]",
25099
25327
  " 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]",
25100
25328
  " 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]",
@@ -25107,7 +25335,7 @@ var CLI_USAGE_LINES = [
25107
25335
  " debugbundle weekly-report update <channel-id> [--day-of-week <day>] [--hour-of-day <0-23>] [--timezone <iana>] [--config-json <json>] [--is-enabled <true|false>] [--auth-file <path>] [--json]",
25108
25336
  " debugbundle weekly-report delete <channel-id> [--auth-file <path>] [--json]",
25109
25337
  " debugbundle capture-policy get --project <id> [--auth-file <path>] [--json]",
25110
- " debugbundle capture-policy set --project <id> [--preset <minimal|balanced|investigative>] [--override <key=value>] [--auth-file <path>] [--json]",
25338
+ " debugbundle capture-policy set --project <id> [--preset <minimal|balanced|investigative>] [--override <key=value>] [--client-error-incidents <preset-default|none|recommended|custom>] [--client-error-statuses <400,401,...>] [--auth-file <path>] [--json]",
25111
25339
  " debugbundle probe activate <project-id> --label-pattern <pattern> [--service <name>] [--environment <name>] [--ttl-seconds <n>] [--trigger-ttl-seconds <n>] [--auth-file <path>] [--json]",
25112
25340
  " debugbundle probe list <project-id> [--auth-file <path>] [--json]",
25113
25341
  " debugbundle probe deactivate <project-id> <activation-id> [--auth-file <path>] [--json]",
@@ -25774,16 +26002,6 @@ async function deleteAlertWithAuthCommand(input, dependencies) {
25774
26002
  }
25775
26003
 
25776
26004
  // src/capture-policy-commands.ts
25777
- var ResolvedCapturePolicySchema = external_exports.object({
25778
- preset: CapturePresetSchema,
25779
- capture_logs: CaptureLogsSchema,
25780
- capture_request_events: CaptureRequestEventsSchema,
25781
- capture_breadcrumbs: CaptureBreadcrumbsSchema,
25782
- capture_probe_events: CaptureProbeEventsSchema
25783
- });
25784
- var CapturePolicyResponseSchema = external_exports.object({
25785
- policy: ResolvedCapturePolicySchema
25786
- });
25787
26005
  var CapturePolicyApiError = class extends Error {
25788
26006
  status;
25789
26007
  constructor(status, message) {
@@ -25813,7 +26031,7 @@ function createCapturePolicyApi(httpClient) {
25813
26031
  if (!parsed.success) {
25814
26032
  throw new CapturePolicyApiError(500, "Invalid capture policy response.");
25815
26033
  }
25816
- return parsed.data.policy;
26034
+ return parsed.data;
25817
26035
  },
25818
26036
  async updateCapturePolicy(input) {
25819
26037
  const response = await httpClient.request({
@@ -25829,7 +26047,7 @@ function createCapturePolicyApi(httpClient) {
25829
26047
  if (!parsed.success) {
25830
26048
  throw new CapturePolicyApiError(500, "Invalid capture policy response.");
25831
26049
  }
25832
- return parsed.data.policy;
26050
+ return parsed.data;
25833
26051
  }
25834
26052
  };
25835
26053
  }
@@ -25848,24 +26066,48 @@ function mapErrorToExitCode5(error) {
25848
26066
  }
25849
26067
  return 1;
25850
26068
  }
25851
- function formatPolicy(policy) {
26069
+ function statusesEqual(left, right) {
26070
+ if (left.length !== right.length) {
26071
+ return false;
26072
+ }
26073
+ return left.every((value, index) => value === right[index]);
26074
+ }
26075
+ function formatStatusList(statuses) {
26076
+ return statuses.length === 0 ? "none" : statuses.join(", ");
26077
+ }
26078
+ function formatClientErrorIncidents(response) {
26079
+ const rawOverride = response.overrides.immediate_client_error_statuses;
26080
+ if (rawOverride === null) {
26081
+ return `preset default (${formatStatusList(response.policy.immediate_client_error_statuses)})`;
26082
+ }
26083
+ if (rawOverride.length === 0) {
26084
+ return "none (explicit)";
26085
+ }
26086
+ if (statusesEqual(rawOverride, RECOMMENDED_IMMEDIATE_CLIENT_ERROR_STATUSES)) {
26087
+ return `recommended (${formatStatusList(rawOverride)})`;
26088
+ }
26089
+ return `custom (${formatStatusList(rawOverride)})`;
26090
+ }
26091
+ function formatPolicy(response) {
26092
+ const policy = response.policy;
25852
26093
  return [
25853
26094
  `preset: ${policy.preset}`,
25854
26095
  `capture_logs: ${policy.capture_logs}`,
25855
26096
  `capture_request_events: ${policy.capture_request_events}`,
25856
26097
  `capture_breadcrumbs: ${policy.capture_breadcrumbs}`,
25857
- `capture_probe_events: ${policy.capture_probe_events}`
26098
+ `capture_probe_events: ${policy.capture_probe_events}`,
26099
+ `client_error_incidents: ${formatClientErrorIncidents(response)}`
25858
26100
  ].join("\n");
25859
26101
  }
25860
26102
  async function getCapturePolicyCommand(input, api) {
25861
26103
  try {
25862
- const policy = await api.getCapturePolicy({
26104
+ const response = await api.getCapturePolicy({
25863
26105
  bearerToken: input.bearerToken,
25864
26106
  projectId: input.projectId
25865
26107
  });
25866
26108
  return {
25867
26109
  exitCode: 0,
25868
- output: input.json ? JSON.stringify({ policy }) : formatPolicy(policy)
26110
+ output: input.json ? JSON.stringify(response) : formatPolicy(response)
25869
26111
  };
25870
26112
  } catch (error) {
25871
26113
  return {
@@ -25883,15 +26125,15 @@ async function setCapturePolicyCommand(input, api) {
25883
26125
  output: "Invalid capture policy update."
25884
26126
  };
25885
26127
  }
25886
- const policy = await api.updateCapturePolicy({
26128
+ const response = await api.updateCapturePolicy({
25887
26129
  bearerToken: input.bearerToken,
25888
26130
  projectId: input.projectId,
25889
26131
  update: parsedUpdate.data
25890
26132
  });
25891
26133
  return {
25892
26134
  exitCode: 0,
25893
- output: input.json ? JSON.stringify({ policy }) : `Capture policy updated.
25894
- ${formatPolicy(policy)}`
26135
+ output: input.json ? JSON.stringify(response) : `Capture policy updated.
26136
+ ${formatPolicy(response)}`
25895
26137
  };
25896
26138
  } catch (error) {
25897
26139
  return {
@@ -26872,8 +27114,153 @@ async function deleteWeeklyReportChannelWithAuthCommand(input, dependencies) {
26872
27114
  });
26873
27115
  }
26874
27116
 
26875
- // src/billing-commands.ts
27117
+ // src/slack-commands.ts
26876
27118
  function mapErrorToExitCode10(error) {
27119
+ if (!(error instanceof SlackApiError)) {
27120
+ return 1;
27121
+ }
27122
+ if (error.status === 401) {
27123
+ return 2;
27124
+ }
27125
+ if (error.status === 404) {
27126
+ return 3;
27127
+ }
27128
+ if (error.status === 400 || error.status === 403) {
27129
+ return 4;
27130
+ }
27131
+ return 1;
27132
+ }
27133
+ function formatSlackDestinationTable(destinations) {
27134
+ if (destinations.length === 0) {
27135
+ return "No Slack destinations found.";
27136
+ }
27137
+ return destinations.map((destination) => {
27138
+ const workspace = destination.slack_team_name ?? destination.slack_team_id;
27139
+ const channel = destination.slack_channel_name ?? destination.slack_channel_id;
27140
+ return `${destination.slack_destination_id} | ${destination.is_active ? "active" : "inactive"} | ${workspace} | ${channel}`;
27141
+ }).join("\n");
27142
+ }
27143
+ async function listSlackDestinationsCommand(input, api) {
27144
+ try {
27145
+ const destinations = await api.listSlackDestinations({
27146
+ bearerToken: input.bearerToken,
27147
+ projectId: input.projectId
27148
+ });
27149
+ return {
27150
+ exitCode: 0,
27151
+ output: input.json ? JSON.stringify({ destinations }) : formatSlackDestinationTable(destinations)
27152
+ };
27153
+ } catch (error) {
27154
+ return { exitCode: mapErrorToExitCode10(error), output: error instanceof Error ? error.message : String(error) };
27155
+ }
27156
+ }
27157
+ async function listSlackDestinationsWithAuthCommand(input, dependencies) {
27158
+ return runAuthenticatedCliCommand(input, {
27159
+ createApi: createAuthenticatedSlackApi,
27160
+ dependencies,
27161
+ runCommand: (authState, api) => listSlackDestinationsCommand(
27162
+ {
27163
+ bearerToken: authState.bearer_token,
27164
+ projectId: input.projectId,
27165
+ ...input.json !== void 0 ? { json: input.json } : {}
27166
+ },
27167
+ api
27168
+ )
27169
+ });
27170
+ }
27171
+ async function getSlackConnectUrlCommand(input, api) {
27172
+ try {
27173
+ const installUrl = await api.getSlackInstallUrl({
27174
+ bearerToken: input.bearerToken,
27175
+ projectId: input.projectId,
27176
+ ...input.returnTo !== void 0 ? { returnTo: input.returnTo } : {}
27177
+ });
27178
+ return {
27179
+ exitCode: 0,
27180
+ output: input.json ? JSON.stringify({ install_url: installUrl }) : installUrl
27181
+ };
27182
+ } catch (error) {
27183
+ return { exitCode: mapErrorToExitCode10(error), output: error instanceof Error ? error.message : String(error) };
27184
+ }
27185
+ }
27186
+ async function getSlackConnectUrlWithAuthCommand(input, dependencies) {
27187
+ return runAuthenticatedCliCommand(input, {
27188
+ createApi: createAuthenticatedSlackApi,
27189
+ dependencies,
27190
+ runCommand: (authState, api) => getSlackConnectUrlCommand(
27191
+ {
27192
+ bearerToken: authState.bearer_token,
27193
+ projectId: input.projectId,
27194
+ ...input.returnTo !== void 0 ? { returnTo: input.returnTo } : {},
27195
+ ...input.json !== void 0 ? { json: input.json } : {}
27196
+ },
27197
+ api
27198
+ )
27199
+ });
27200
+ }
27201
+ async function testSlackDestinationCommand(input, api) {
27202
+ try {
27203
+ const delivery = await api.testSlackDestination({
27204
+ bearerToken: input.bearerToken,
27205
+ projectId: input.projectId,
27206
+ destinationId: input.destinationId
27207
+ });
27208
+ return {
27209
+ exitCode: 0,
27210
+ output: input.json ? JSON.stringify({ delivery }) : `Slack test message delivered for destination: ${input.destinationId}`
27211
+ };
27212
+ } catch (error) {
27213
+ return { exitCode: mapErrorToExitCode10(error), output: error instanceof Error ? error.message : String(error) };
27214
+ }
27215
+ }
27216
+ async function testSlackDestinationWithAuthCommand(input, dependencies) {
27217
+ return runAuthenticatedCliCommand(input, {
27218
+ createApi: createAuthenticatedSlackApi,
27219
+ dependencies,
27220
+ runCommand: (authState, api) => testSlackDestinationCommand(
27221
+ {
27222
+ bearerToken: authState.bearer_token,
27223
+ projectId: input.projectId,
27224
+ destinationId: input.destinationId,
27225
+ ...input.json !== void 0 ? { json: input.json } : {}
27226
+ },
27227
+ api
27228
+ )
27229
+ });
27230
+ }
27231
+ async function deleteSlackDestinationCommand(input, api) {
27232
+ try {
27233
+ const deleted = await api.deleteSlackDestination({
27234
+ bearerToken: input.bearerToken,
27235
+ projectId: input.projectId,
27236
+ destinationId: input.destinationId
27237
+ });
27238
+ return {
27239
+ exitCode: 0,
27240
+ output: input.json ? JSON.stringify({ destination: deleted }) : `Slack destination deleted: ${deleted.slack_destination_id}`
27241
+ };
27242
+ } catch (error) {
27243
+ return { exitCode: mapErrorToExitCode10(error), output: error instanceof Error ? error.message : String(error) };
27244
+ }
27245
+ }
27246
+ async function deleteSlackDestinationWithAuthCommand(input, dependencies) {
27247
+ return runAuthenticatedCliCommand(input, {
27248
+ createApi: createAuthenticatedSlackApi,
27249
+ dependencies,
27250
+ runCommand: (authState, api) => deleteSlackDestinationCommand(
27251
+ {
27252
+ bearerToken: authState.bearer_token,
27253
+ projectId: input.projectId,
27254
+ destinationId: input.destinationId,
27255
+ ...input.json !== void 0 ? { json: input.json } : {}
27256
+ },
27257
+ api
27258
+ )
27259
+ });
27260
+ }
27261
+
27262
+ // src/billing-commands.ts
27263
+ function mapErrorToExitCode11(error) {
26877
27264
  if (!(error instanceof BillingApiError)) {
26878
27265
  return 1;
26879
27266
  }
@@ -26921,7 +27308,7 @@ async function getBillingSummaryCommand(input, api) {
26921
27308
  output: input.json ? JSON.stringify({ billing }) : formatBillingSummary(billing)
26922
27309
  };
26923
27310
  } catch (error) {
26924
- return { exitCode: mapErrorToExitCode10(error), output: error instanceof Error ? error.message : String(error) };
27311
+ return { exitCode: mapErrorToExitCode11(error), output: error instanceof Error ? error.message : String(error) };
26925
27312
  }
26926
27313
  }
26927
27314
  async function increaseBillingCapacityCommand(input, api) {
@@ -26936,7 +27323,7 @@ async function increaseBillingCapacityCommand(input, api) {
26936
27323
  ${formatBillingSummary(billing)}`
26937
27324
  };
26938
27325
  } catch (error) {
26939
- return { exitCode: mapErrorToExitCode10(error), output: error instanceof Error ? error.message : String(error) };
27326
+ return { exitCode: mapErrorToExitCode11(error), output: error instanceof Error ? error.message : String(error) };
26940
27327
  }
26941
27328
  }
26942
27329
  async function scheduleBillingCapacityReductionCommand(input, api) {
@@ -26951,7 +27338,7 @@ async function scheduleBillingCapacityReductionCommand(input, api) {
26951
27338
  ${formatBillingSummary(billing)}`
26952
27339
  };
26953
27340
  } catch (error) {
26954
- return { exitCode: mapErrorToExitCode10(error), output: error instanceof Error ? error.message : String(error) };
27341
+ return { exitCode: mapErrorToExitCode11(error), output: error instanceof Error ? error.message : String(error) };
26955
27342
  }
26956
27343
  }
26957
27344
  async function cancelBillingCapacityReductionCommand(input, api) {
@@ -26963,7 +27350,7 @@ async function cancelBillingCapacityReductionCommand(input, api) {
26963
27350
  ${formatBillingSummary(billing)}`
26964
27351
  };
26965
27352
  } catch (error) {
26966
- return { exitCode: mapErrorToExitCode10(error), output: error instanceof Error ? error.message : String(error) };
27353
+ return { exitCode: mapErrorToExitCode11(error), output: error instanceof Error ? error.message : String(error) };
26967
27354
  }
26968
27355
  }
26969
27356
  async function getBillingSummaryWithAuthCommand(input, dependencies) {
@@ -27124,7 +27511,7 @@ function formatInvitesTable(invites) {
27124
27511
  }
27125
27512
  return invites.map((i) => `${i.invite_id} | ${i.email} | ${i.role} | expires=${i.expires_at}`).join("\n");
27126
27513
  }
27127
- function mapErrorToExitCode11(error) {
27514
+ function mapErrorToExitCode12(error) {
27128
27515
  if (!(error instanceof MemberApiError)) {
27129
27516
  return 1;
27130
27517
  }
@@ -27151,7 +27538,7 @@ async function listMembersCommand(input, api) {
27151
27538
  };
27152
27539
  } catch (error) {
27153
27540
  return {
27154
- exitCode: mapErrorToExitCode11(error),
27541
+ exitCode: mapErrorToExitCode12(error),
27155
27542
  output: error instanceof MemberApiError ? error.code : String(error)
27156
27543
  };
27157
27544
  }
@@ -27165,7 +27552,7 @@ async function listInvitesCommand(input, api) {
27165
27552
  };
27166
27553
  } catch (error) {
27167
27554
  return {
27168
- exitCode: mapErrorToExitCode11(error),
27555
+ exitCode: mapErrorToExitCode12(error),
27169
27556
  output: error instanceof MemberApiError ? error.code : String(error)
27170
27557
  };
27171
27558
  }
@@ -27179,7 +27566,7 @@ async function inviteMemberCommand(input, api) {
27179
27566
  };
27180
27567
  } catch (error) {
27181
27568
  return {
27182
- exitCode: mapErrorToExitCode11(error),
27569
+ exitCode: mapErrorToExitCode12(error),
27183
27570
  output: error instanceof MemberApiError ? error.code : String(error)
27184
27571
  };
27185
27572
  }
@@ -27193,7 +27580,7 @@ async function cancelInviteCommand(input, api) {
27193
27580
  };
27194
27581
  } catch (error) {
27195
27582
  return {
27196
- exitCode: mapErrorToExitCode11(error),
27583
+ exitCode: mapErrorToExitCode12(error),
27197
27584
  output: error instanceof MemberApiError ? error.code : String(error)
27198
27585
  };
27199
27586
  }
@@ -27207,7 +27594,7 @@ async function updateMemberRoleCommand(input, api) {
27207
27594
  };
27208
27595
  } catch (error) {
27209
27596
  return {
27210
- exitCode: mapErrorToExitCode11(error),
27597
+ exitCode: mapErrorToExitCode12(error),
27211
27598
  output: error instanceof MemberApiError ? error.code : String(error)
27212
27599
  };
27213
27600
  }
@@ -27221,7 +27608,7 @@ async function removeMemberCommand(input, api) {
27221
27608
  };
27222
27609
  } catch (error) {
27223
27610
  return {
27224
- exitCode: mapErrorToExitCode11(error),
27611
+ exitCode: mapErrorToExitCode12(error),
27225
27612
  output: error instanceof MemberApiError ? error.code : String(error)
27226
27613
  };
27227
27614
  }
@@ -27370,7 +27757,7 @@ function createProbeApi(httpClient) {
27370
27757
  }
27371
27758
  };
27372
27759
  }
27373
- function mapErrorToExitCode12(error) {
27760
+ function mapErrorToExitCode13(error) {
27374
27761
  if (!(error instanceof ProbeApiError)) {
27375
27762
  return 1;
27376
27763
  }
@@ -27423,7 +27810,7 @@ async function activateProbeCommand(input, api) {
27423
27810
  Trigger token: ${result.trigger_token}`
27424
27811
  };
27425
27812
  } catch (error) {
27426
- return { exitCode: mapErrorToExitCode12(error), output: error instanceof Error ? error.message : String(error) };
27813
+ return { exitCode: mapErrorToExitCode13(error), output: error instanceof Error ? error.message : String(error) };
27427
27814
  }
27428
27815
  }
27429
27816
  async function listActiveProbesCommand(input, api) {
@@ -27443,7 +27830,7 @@ async function listActiveProbesCommand(input, api) {
27443
27830
  output: result.activations.map((a) => `${a.activation_id} ${a.label_pattern} (${a.service}/${a.environment}) expires ${a.expires_at}`).join("\n")
27444
27831
  };
27445
27832
  } catch (error) {
27446
- return { exitCode: mapErrorToExitCode12(error), output: error instanceof Error ? error.message : String(error) };
27833
+ return { exitCode: mapErrorToExitCode13(error), output: error instanceof Error ? error.message : String(error) };
27447
27834
  }
27448
27835
  }
27449
27836
  async function deactivateProbeCommand(input, api) {
@@ -27461,7 +27848,7 @@ async function deactivateProbeCommand(input, api) {
27461
27848
  output: result.deactivated ? "Probe deactivated." : "Probe was already inactive."
27462
27849
  };
27463
27850
  } catch (error) {
27464
- return { exitCode: mapErrorToExitCode12(error), output: error instanceof Error ? error.message : String(error) };
27851
+ return { exitCode: mapErrorToExitCode13(error), output: error instanceof Error ? error.message : String(error) };
27465
27852
  }
27466
27853
  }
27467
27854
  async function createAuthenticatedProbeApi(input, dependencies) {
@@ -27511,7 +27898,7 @@ async function deactivateProbeWithAuthCommand(input, dependencies) {
27511
27898
  }
27512
27899
 
27513
27900
  // src/github-commands.ts
27514
- function mapErrorToExitCode13(error) {
27901
+ function mapErrorToExitCode14(error) {
27515
27902
  if (!(error instanceof GitHubManagementApiError)) {
27516
27903
  return 1;
27517
27904
  }
@@ -27572,7 +27959,7 @@ async function getGitHubStatusCommand(input, api) {
27572
27959
  ${formatProjectRepo(repo)}`
27573
27960
  };
27574
27961
  } catch (error) {
27575
- return { exitCode: mapErrorToExitCode13(error), output: error instanceof Error ? error.message : String(error) };
27962
+ return { exitCode: mapErrorToExitCode14(error), output: error instanceof Error ? error.message : String(error) };
27576
27963
  }
27577
27964
  }
27578
27965
  async function listGitHubRepositoriesCommand(input, api) {
@@ -27583,7 +27970,7 @@ async function listGitHubRepositoriesCommand(input, api) {
27583
27970
  output: input.json ? JSON.stringify({ repositories }) : repositories.length === 0 ? "No GitHub repositories found." : repositories.map((repository) => `${repository.full_name} (${repository.default_branch})`).join("\n")
27584
27971
  };
27585
27972
  } catch (error) {
27586
- return { exitCode: mapErrorToExitCode13(error), output: error instanceof Error ? error.message : String(error) };
27973
+ return { exitCode: mapErrorToExitCode14(error), output: error instanceof Error ? error.message : String(error) };
27587
27974
  }
27588
27975
  }
27589
27976
  async function setProjectGitHubRepoCommand(input, api) {
@@ -27606,7 +27993,7 @@ async function setProjectGitHubRepoCommand(input, api) {
27606
27993
  output: input.json ? JSON.stringify({ repo: assignedRepo }) : `Project repo set: ${formatProjectRepo(assignedRepo)}`
27607
27994
  };
27608
27995
  } catch (error) {
27609
- return { exitCode: mapErrorToExitCode13(error), output: error instanceof Error ? error.message : String(error) };
27996
+ return { exitCode: mapErrorToExitCode14(error), output: error instanceof Error ? error.message : String(error) };
27610
27997
  }
27611
27998
  }
27612
27999
  async function removeProjectGitHubRepoCommand(input, api) {
@@ -27617,7 +28004,7 @@ async function removeProjectGitHubRepoCommand(input, api) {
27617
28004
  output: input.json ? JSON.stringify({ removed: true, project_id: input.projectId }) : `Project repo removed: ${input.projectId}`
27618
28005
  };
27619
28006
  } catch (error) {
27620
- return { exitCode: mapErrorToExitCode13(error), output: error instanceof Error ? error.message : String(error) };
28007
+ return { exitCode: mapErrorToExitCode14(error), output: error instanceof Error ? error.message : String(error) };
27621
28008
  }
27622
28009
  }
27623
28010
  async function listProjectGitHubRulesCommand(input, api) {
@@ -27631,7 +28018,7 @@ async function listProjectGitHubRulesCommand(input, api) {
27631
28018
  output: input.json ? JSON.stringify({ rules }) : formatGitHubRuleTable(rules)
27632
28019
  };
27633
28020
  } catch (error) {
27634
- return { exitCode: mapErrorToExitCode13(error), output: error instanceof Error ? error.message : String(error) };
28021
+ return { exitCode: mapErrorToExitCode14(error), output: error instanceof Error ? error.message : String(error) };
27635
28022
  }
27636
28023
  }
27637
28024
  async function listProjectGitHubDeliveriesCommand(input, api) {
@@ -27647,7 +28034,7 @@ async function listProjectGitHubDeliveriesCommand(input, api) {
27647
28034
  output: input.json ? JSON.stringify({ deliveries }) : formatGitHubDeliveryTable(deliveries)
27648
28035
  };
27649
28036
  } catch (error) {
27650
- return { exitCode: mapErrorToExitCode13(error), output: error instanceof Error ? error.message : String(error) };
28037
+ return { exitCode: mapErrorToExitCode14(error), output: error instanceof Error ? error.message : String(error) };
27651
28038
  }
27652
28039
  }
27653
28040
  async function retryProjectGitHubDeliveryCommand(input, api) {
@@ -27662,7 +28049,7 @@ async function retryProjectGitHubDeliveryCommand(input, api) {
27662
28049
  output: input.json ? JSON.stringify({ delivery }) : `GitHub delivery retried: ${delivery.delivery_id} | ${delivery.status}`
27663
28050
  };
27664
28051
  } catch (error) {
27665
- return { exitCode: mapErrorToExitCode13(error), output: error instanceof Error ? error.message : String(error) };
28052
+ return { exitCode: mapErrorToExitCode14(error), output: error instanceof Error ? error.message : String(error) };
27666
28053
  }
27667
28054
  }
27668
28055
  async function createProjectGitHubRuleCommand(input, api) {
@@ -27685,7 +28072,7 @@ async function createProjectGitHubRuleCommand(input, api) {
27685
28072
  output: input.json ? JSON.stringify({ rule }) : `GitHub rule created: ${rule.rule_id}`
27686
28073
  };
27687
28074
  } catch (error) {
27688
- return { exitCode: mapErrorToExitCode13(error), output: error instanceof Error ? error.message : String(error) };
28075
+ return { exitCode: mapErrorToExitCode14(error), output: error instanceof Error ? error.message : String(error) };
27689
28076
  }
27690
28077
  }
27691
28078
  async function updateProjectGitHubRuleCommand(input, api) {
@@ -27709,7 +28096,7 @@ async function updateProjectGitHubRuleCommand(input, api) {
27709
28096
  output: input.json ? JSON.stringify({ rule }) : `GitHub rule updated: ${rule.rule_id}`
27710
28097
  };
27711
28098
  } catch (error) {
27712
- return { exitCode: mapErrorToExitCode13(error), output: error instanceof Error ? error.message : String(error) };
28099
+ return { exitCode: mapErrorToExitCode14(error), output: error instanceof Error ? error.message : String(error) };
27713
28100
  }
27714
28101
  }
27715
28102
  async function deleteProjectGitHubRuleCommand(input, api) {
@@ -27724,7 +28111,7 @@ async function deleteProjectGitHubRuleCommand(input, api) {
27724
28111
  output: input.json ? JSON.stringify({ deleted: true, project_id: input.projectId, rule_id: input.ruleId }) : `GitHub rule deleted: ${input.ruleId}`
27725
28112
  };
27726
28113
  } catch (error) {
27727
- return { exitCode: mapErrorToExitCode13(error), output: error instanceof Error ? error.message : String(error) };
28114
+ return { exitCode: mapErrorToExitCode14(error), output: error instanceof Error ? error.message : String(error) };
27728
28115
  }
27729
28116
  }
27730
28117
  async function getGitHubStatusWithAuthCommand(input, dependencies) {
@@ -28255,6 +28642,28 @@ async function handleTokenCommand(parsedArgv, dependencies) {
28255
28642
  throw new CliInputError("Unknown token command.");
28256
28643
  }
28257
28644
  async function handleCapturePolicyCommand(parsedArgv, dependencies) {
28645
+ function parseClientErrorStatusesOption(value) {
28646
+ const parts = value.split(",").map((part) => part.trim()).filter((part) => part.length > 0);
28647
+ if (parts.length === 0) {
28648
+ throw new CliInputError("Invalid value for --client-error-statuses.");
28649
+ }
28650
+ const statuses = [];
28651
+ for (const part of parts) {
28652
+ if (!/^\d+$/.test(part)) {
28653
+ throw new CliInputError("Invalid value for --client-error-statuses.");
28654
+ }
28655
+ const status = Number(part);
28656
+ if (!Number.isInteger(status) || status < 400 || status > 499) {
28657
+ throw new CliInputError("Invalid value for --client-error-statuses.");
28658
+ }
28659
+ statuses.push(status);
28660
+ }
28661
+ const normalized = Array.from(new Set(statuses)).sort((left, right) => left - right);
28662
+ if (normalized.length > 12) {
28663
+ throw new CliInputError("Invalid value for --client-error-statuses.");
28664
+ }
28665
+ return normalized;
28666
+ }
28258
28667
  const action = requirePositional(parsedArgv, 1, "action");
28259
28668
  if (action === "get") {
28260
28669
  expectNoUnknownOptions(parsedArgv, ["auth-file", "json", "project"]);
@@ -28268,7 +28677,15 @@ async function handleCapturePolicyCommand(parsedArgv, dependencies) {
28268
28677
  );
28269
28678
  }
28270
28679
  if (action === "set") {
28271
- expectNoUnknownOptions(parsedArgv, ["auth-file", "json", "project", "preset", "override"]);
28680
+ expectNoUnknownOptions(parsedArgv, [
28681
+ "auth-file",
28682
+ "json",
28683
+ "project",
28684
+ "preset",
28685
+ "override",
28686
+ "client-error-incidents",
28687
+ "client-error-statuses"
28688
+ ]);
28272
28689
  ensureNoExtraPositionals(parsedArgv, 2);
28273
28690
  const projectId = readStringOption(parsedArgv, "project");
28274
28691
  if (projectId === void 0) {
@@ -28292,6 +28709,32 @@ async function handleCapturePolicyCommand(parsedArgv, dependencies) {
28292
28709
  }
28293
28710
  update[key] = rawValue === "null" ? null : rawValue;
28294
28711
  }
28712
+ const clientErrorIncidents = readStringOption(parsedArgv, "client-error-incidents");
28713
+ const clientErrorStatuses = readStringOption(parsedArgv, "client-error-statuses");
28714
+ if (clientErrorStatuses !== void 0 && clientErrorIncidents !== "custom") {
28715
+ throw new CliInputError("Use --client-error-statuses only with --client-error-incidents custom.");
28716
+ }
28717
+ if (clientErrorIncidents !== void 0) {
28718
+ switch (clientErrorIncidents) {
28719
+ case "preset-default":
28720
+ update.immediate_client_error_statuses = null;
28721
+ break;
28722
+ case "none":
28723
+ update.immediate_client_error_statuses = [];
28724
+ break;
28725
+ case "recommended":
28726
+ update.immediate_client_error_statuses = [...RECOMMENDED_IMMEDIATE_CLIENT_ERROR_STATUSES];
28727
+ break;
28728
+ case "custom":
28729
+ if (clientErrorStatuses === void 0) {
28730
+ throw new CliInputError("Missing required option --client-error-statuses.");
28731
+ }
28732
+ update.immediate_client_error_statuses = parseClientErrorStatusesOption(clientErrorStatuses);
28733
+ break;
28734
+ default:
28735
+ throw new CliInputError("Invalid value for --client-error-incidents.");
28736
+ }
28737
+ }
28295
28738
  if (Object.keys(update).length === 0) {
28296
28739
  throw new CliInputError("At least one capture policy field must be provided.");
28297
28740
  }
@@ -28722,6 +29165,67 @@ async function handleAlertCommand(parsedArgv, dependencies) {
28722
29165
  }
28723
29166
  throw new CliInputError("Unknown alert command.");
28724
29167
  }
29168
+ async function handleSlackCommand(parsedArgv, dependencies) {
29169
+ const action = requirePositional(parsedArgv, 1, "action");
29170
+ if (action === "list") {
29171
+ expectNoUnknownOptions(parsedArgv, ["auth-file", "json", "project-id"]);
29172
+ ensureNoExtraPositionals(parsedArgv, 2);
29173
+ const projectId = readStringOption(parsedArgv, "project-id");
29174
+ if (projectId === void 0) {
29175
+ throw new CliInputError("Missing required option --project-id.");
29176
+ }
29177
+ return await (dependencies.listSlackDestinationsCommand ?? listSlackDestinationsWithAuthCommand)(
29178
+ appendCommonAuthOptions(parsedArgv, {
29179
+ projectId
29180
+ })
29181
+ );
29182
+ }
29183
+ if (action === "connect-url") {
29184
+ expectNoUnknownOptions(parsedArgv, ["auth-file", "json", "project-id", "return-to"]);
29185
+ ensureNoExtraPositionals(parsedArgv, 2);
29186
+ const projectId = readStringOption(parsedArgv, "project-id");
29187
+ if (projectId === void 0) {
29188
+ throw new CliInputError("Missing required option --project-id.");
29189
+ }
29190
+ const input = appendCommonAuthOptions(parsedArgv, {
29191
+ projectId
29192
+ });
29193
+ const returnTo = readStringOption(parsedArgv, "return-to");
29194
+ if (returnTo !== void 0) {
29195
+ input.returnTo = returnTo;
29196
+ }
29197
+ return await (dependencies.getSlackConnectUrlCommand ?? getSlackConnectUrlWithAuthCommand)(input);
29198
+ }
29199
+ if (action === "test") {
29200
+ expectNoUnknownOptions(parsedArgv, ["auth-file", "json", "project-id"]);
29201
+ ensureNoExtraPositionals(parsedArgv, 3);
29202
+ const projectId = readStringOption(parsedArgv, "project-id");
29203
+ if (projectId === void 0) {
29204
+ throw new CliInputError("Missing required option --project-id.");
29205
+ }
29206
+ return await (dependencies.testSlackDestinationCommand ?? testSlackDestinationWithAuthCommand)(
29207
+ appendCommonAuthOptions(parsedArgv, {
29208
+ projectId,
29209
+ destinationId: requirePositional(parsedArgv, 2, "destination-id")
29210
+ })
29211
+ );
29212
+ }
29213
+ if (action === "delete") {
29214
+ expectNoUnknownOptions(parsedArgv, ["auth-file", "json", "project-id"]);
29215
+ ensureNoExtraPositionals(parsedArgv, 3);
29216
+ const projectId = readStringOption(parsedArgv, "project-id");
29217
+ if (projectId === void 0) {
29218
+ throw new CliInputError("Missing required option --project-id.");
29219
+ }
29220
+ return await (dependencies.deleteSlackDestinationCommand ?? deleteSlackDestinationWithAuthCommand)(
29221
+ appendCommonAuthOptions(parsedArgv, {
29222
+ projectId,
29223
+ destinationId: requirePositional(parsedArgv, 2, "destination-id")
29224
+ })
29225
+ );
29226
+ }
29227
+ throw new CliInputError("Unknown slack command.");
29228
+ }
28725
29229
  async function handleWeeklyReportCommand(parsedArgv, dependencies) {
28726
29230
  const action = requirePositional(parsedArgv, 1, "action");
28727
29231
  if (action === "list") {
@@ -28777,10 +29281,20 @@ async function handleWeeklyReportCommand(parsedArgv, dependencies) {
28777
29281
  if (config === void 0 || typeof config !== "object" || config === null) {
28778
29282
  throw new CliInputError("Missing required option --config-json.");
28779
29283
  }
29284
+ let weeklyReportConfig;
29285
+ if (channel === "slack") {
29286
+ if (typeof config["slack_destination_id"] === "string") {
29287
+ weeklyReportConfig = { slackDestinationId: String(config["slack_destination_id"]) };
29288
+ } else {
29289
+ weeklyReportConfig = { webhookUrl: String(config["webhook_url"]) };
29290
+ }
29291
+ } else {
29292
+ weeklyReportConfig = { to: config.to };
29293
+ }
28780
29294
  const input = appendCommonAuthOptions(parsedArgv, {
28781
29295
  projectId,
28782
29296
  channel,
28783
- config: channel === "slack" ? { webhookUrl: String(config["webhook_url"]) } : { to: config.to },
29297
+ config: weeklyReportConfig,
28784
29298
  schedule: {
28785
29299
  dayOfWeek,
28786
29300
  hourOfDay,
@@ -28821,7 +29335,7 @@ async function handleWeeklyReportCommand(parsedArgv, dependencies) {
28821
29335
  if (typeof config !== "object" || config === null) {
28822
29336
  throw new CliInputError("Invalid value for --config-json.");
28823
29337
  }
28824
- input.config = "webhook_url" in config ? { webhookUrl: String(config["webhook_url"]) } : { to: config.to };
29338
+ 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 };
28825
29339
  }
28826
29340
  const isEnabled = readBooleanStringOption(parsedArgv, "is-enabled");
28827
29341
  if (isEnabled !== void 0) {
@@ -29236,6 +29750,9 @@ ${formatUsage()}`
29236
29750
  if (command === "alert") {
29237
29751
  return await handleAlertCommand(parsedArgv, dependencies);
29238
29752
  }
29753
+ if (command === "slack") {
29754
+ return await handleSlackCommand(parsedArgv, dependencies);
29755
+ }
29239
29756
  if (command === "webhook") {
29240
29757
  return await handleWebhookCommand(parsedArgv, dependencies);
29241
29758
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@debugbundle/cli",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "private": false,
5
5
  "description": "Command-line interface for DebugBundle",
6
6
  "license": "AGPL-3.0-only",