@debugbundle/cli 1.1.0 → 1.1.2

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 +435 -38
  2. package/package.json +1 -1
package/dist/main.cjs CHANGED
@@ -14605,6 +14605,95 @@ function getRequestAnomalyThreshold(input2) {
14605
14605
  return null;
14606
14606
  }
14607
14607
 
14608
+ // ../../packages/shared-types/src/request-failure-noise.ts
14609
+ function isLowValueExternalProbeRequestFailure404(input2) {
14610
+ if (input2.responseStatus !== 404 || input2.httpMethod.toUpperCase() !== "GET") {
14611
+ return false;
14612
+ }
14613
+ const normalizedRoute = input2.routeTemplate.toLowerCase().replace(/\/+$/, "") || "/";
14614
+ const normalizedPath = normalizePath(input2.requestPath ?? input2.routeTemplate);
14615
+ const routesToCheck = /* @__PURE__ */ new Set([normalizedRoute, normalizedPath]);
14616
+ for (const route of routesToCheck) {
14617
+ if (isRouteOnlyExternalProbe(route)) {
14618
+ return true;
14619
+ }
14620
+ }
14621
+ return isDirectIpRequest(input2.headers ?? null) && [...routesToCheck].some(isGenericDirectIpProbeRoute);
14622
+ }
14623
+ function normalizePath(path) {
14624
+ const withoutQuery = path.split("?")[0] ?? path;
14625
+ return withoutQuery.toLowerCase().replace(/\/+$/, "") || "/";
14626
+ }
14627
+ function isRouteOnlyExternalProbe(normalizedRoute) {
14628
+ const exactRoutes = /* @__PURE__ */ new Set([
14629
+ "/.env",
14630
+ "/__debug__/render_panel",
14631
+ "/actuator",
14632
+ "/autodiscover/autodiscover.json",
14633
+ "/developmentserver/metadatauploader",
14634
+ "/cpanel",
14635
+ "/favicon.ico",
14636
+ "/geoserver/web",
14637
+ "/hnap1",
14638
+ "/logon/logonpoint/index.html",
14639
+ "/owa/auth/logon.aspx",
14640
+ "/robots.txt",
14641
+ "/rdweb/pages",
14642
+ "/web",
14643
+ "/webclient/login.xhtml",
14644
+ "/webconsole",
14645
+ "/webui",
14646
+ "/whm",
14647
+ "/wp-admin",
14648
+ "/wp-login.php",
14649
+ "/wsman",
14650
+ "/xmlrpc.php"
14651
+ ]);
14652
+ if (exactRoutes.has(normalizedRoute)) {
14653
+ return true;
14654
+ }
14655
+ return normalizedRoute.includes("/.git/") || normalizedRoute.includes("/.svn/") || normalizedRoute.includes("/api_keys") || normalizedRoute.includes("/backup/api_keys") || normalizedRoute.includes("/phpmyadmin") || normalizedRoute.includes("/pma/") || normalizedRoute.includes("/vendor/phpunit/") || normalizedRoute.startsWith("/autodiscover/") || normalizedRoute.startsWith("/cgi-bin/") || normalizedRoute.startsWith("/ecp/") || normalizedRoute.endsWith("/.git/config") || normalizedRoute.endsWith("/composer.json") || normalizedRoute.endsWith("/composer.lock") || normalizedRoute.endsWith("/package-lock.json") || normalizedRoute.endsWith("/package.json") || normalizedRoute.endsWith("/server-status") || normalizedRoute.includes("wp-config") || normalizedRoute.startsWith("/owa/") || normalizedRoute.startsWith("/rdweb/") || normalizedRoute.startsWith("/vpn/") || normalizedRoute.startsWith("/wp-") || isSensitiveBackupFileProbe(normalizedRoute);
14656
+ }
14657
+ function isSensitiveBackupFileProbe(normalizedRoute) {
14658
+ if (!/\.(?:bak|backup|dump|old|orig|save|sql|swp|tar|tar\.gz|zip)$/.test(normalizedRoute)) {
14659
+ return false;
14660
+ }
14661
+ return /(?:^|\/|\.)(?:backup|config|database|db|dump|env|secret|site|www|wp-config)(?:\/|\.|_|-|$)/.test(normalizedRoute);
14662
+ }
14663
+ function isGenericDirectIpProbeRoute(normalizedRoute) {
14664
+ return [
14665
+ "/admin",
14666
+ "/administrator",
14667
+ "/login",
14668
+ "/logincheck",
14669
+ "/remote/logincheck"
14670
+ ].includes(normalizedRoute);
14671
+ }
14672
+ function isDirectIpRequest(headers) {
14673
+ if (headers === null) {
14674
+ return false;
14675
+ }
14676
+ const host = readHeader(headers, "x-forwarded-host") ?? readHeader(headers, "host");
14677
+ if (host === null) {
14678
+ return false;
14679
+ }
14680
+ return isIpLikeHost(host);
14681
+ }
14682
+ function readHeader(headers, name) {
14683
+ const direct = headers[name] ?? headers[name.toLowerCase()];
14684
+ if (typeof direct === "string") {
14685
+ return direct;
14686
+ }
14687
+ if (Array.isArray(direct) && typeof direct[0] === "string") {
14688
+ return direct[0];
14689
+ }
14690
+ return null;
14691
+ }
14692
+ function isIpLikeHost(value) {
14693
+ const host = value.trim().replace(/:\d+$/, "");
14694
+ return /^(?:\d{1,3}\.){3}\d{1,3}$/.test(host) || /^\[[0-9a-f:]+\]$/i.test(host) || host.includes(":") && /^[0-9a-f:]+$/i.test(host);
14695
+ }
14696
+
14608
14697
  // ../../packages/shared-types/src/capture-rules.ts
14609
14698
  var CAPTURE_RULE_EVENT_TYPES = [
14610
14699
  "backend_exception",
@@ -16413,6 +16502,9 @@ var IncidentsResponseSchema = external_exports.object({
16413
16502
  var IncidentResponseSchema = external_exports.object({
16414
16503
  incident: IncidentSchema
16415
16504
  }).strict();
16505
+ var BulkIncidentResponseSchema = external_exports.object({
16506
+ incidents: external_exports.array(IncidentSchema)
16507
+ }).strict();
16416
16508
  var ImprovementResponseSchema = external_exports.object({
16417
16509
  improvement: ImprovementSchema
16418
16510
  }).strict();
@@ -16619,6 +16711,20 @@ function createRetrievalApi(client) {
16619
16711
  );
16620
16712
  return parsed.incident;
16621
16713
  },
16714
+ async resolveIncidents(input2) {
16715
+ const parsed = await expectParsed(
16716
+ client.request({
16717
+ method: "POST",
16718
+ path: "/v1/incidents/resolve",
16719
+ bearerToken: input2.bearerToken,
16720
+ body: {
16721
+ incident_ids: input2.incidentIds
16722
+ }
16723
+ }),
16724
+ BulkIncidentResponseSchema
16725
+ );
16726
+ return parsed.incidents;
16727
+ },
16622
16728
  async reopenIncident(input2) {
16623
16729
  const parsed = await expectParsed(
16624
16730
  client.request({
@@ -16630,6 +16736,20 @@ function createRetrievalApi(client) {
16630
16736
  );
16631
16737
  return parsed.incident;
16632
16738
  },
16739
+ async reopenIncidents(input2) {
16740
+ const parsed = await expectParsed(
16741
+ client.request({
16742
+ method: "POST",
16743
+ path: "/v1/incidents/reopen",
16744
+ bearerToken: input2.bearerToken,
16745
+ body: {
16746
+ incident_ids: input2.incidentIds
16747
+ }
16748
+ }),
16749
+ BulkIncidentResponseSchema
16750
+ );
16751
+ return parsed.incidents;
16752
+ },
16633
16753
  async getBundle(input2) {
16634
16754
  const bundle = await expectParsed(
16635
16755
  client.request({
@@ -18412,7 +18532,8 @@ var STORAGE_BOOTSTRAP_STATEMENTS = [
18412
18532
  `
18413
18533
  CREATE TABLE github_dispatch_deliveries (
18414
18534
  id uuid PRIMARY KEY,
18415
- rule_id uuid NOT NULL REFERENCES github_dispatch_rules(id) ON DELETE CASCADE,
18535
+ rule_id uuid NOT NULL,
18536
+ rule_name text NOT NULL,
18416
18537
  project_id uuid NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
18417
18538
  incident_id uuid REFERENCES incidents(id) ON DELETE CASCADE,
18418
18539
  improvement_opportunity_id uuid REFERENCES improvement_opportunities(id) ON DELETE CASCADE,
@@ -18500,6 +18621,26 @@ var STORAGE_BOOTSTRAP_STATEMENTS = [
18500
18621
  `
18501
18622
  CREATE INDEX trial_lifecycle_events_org_event_created_idx
18502
18623
  ON trial_lifecycle_events (organization_id, event_type, created_at DESC)
18624
+ `,
18625
+ `
18626
+ CREATE TABLE plan_cleanup_tasks (
18627
+ id uuid PRIMARY KEY,
18628
+ organization_id uuid NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
18629
+ project_id uuid NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
18630
+ cleanup_type text NOT NULL
18631
+ CHECK (cleanup_type IN ('delete_improvement_bundle_objects')),
18632
+ attempt_count integer NOT NULL DEFAULT 0,
18633
+ last_error text,
18634
+ next_attempt_at timestamptz NOT NULL DEFAULT now(),
18635
+ completed_at timestamptz,
18636
+ created_at timestamptz NOT NULL DEFAULT now(),
18637
+ updated_at timestamptz NOT NULL DEFAULT now(),
18638
+ UNIQUE (project_id, cleanup_type)
18639
+ )
18640
+ `,
18641
+ `
18642
+ CREATE INDEX plan_cleanup_tasks_pending_idx
18643
+ ON plan_cleanup_tasks (completed_at, next_attempt_at, created_at)
18503
18644
  `
18504
18645
  ];
18505
18646
 
@@ -19151,6 +19292,50 @@ var STORAGE_SCHEMA_MIGRATIONS = [
19151
19292
  )
19152
19293
  `
19153
19294
  ]
19295
+ }),
19296
+ defineStorageSchemaMigration({
19297
+ id: "202606050001_preserve_github_dispatch_history_when_rules_are_deleted",
19298
+ description: "Snapshot GitHub rule names onto deliveries and decouple delivery history from live rule rows.",
19299
+ statements: [
19300
+ "ALTER TABLE github_dispatch_deliveries ADD COLUMN IF NOT EXISTS rule_name text",
19301
+ `
19302
+ UPDATE github_dispatch_deliveries deliveries
19303
+ SET rule_name = rules.name
19304
+ FROM github_dispatch_rules rules
19305
+ WHERE deliveries.rule_id = rules.id
19306
+ AND deliveries.rule_name IS NULL
19307
+ `,
19308
+ "ALTER TABLE github_dispatch_deliveries ALTER COLUMN rule_name SET DEFAULT ''",
19309
+ "UPDATE github_dispatch_deliveries SET rule_name = '' WHERE rule_name IS NULL",
19310
+ "ALTER TABLE github_dispatch_deliveries ALTER COLUMN rule_name SET NOT NULL",
19311
+ "ALTER TABLE github_dispatch_deliveries DROP CONSTRAINT IF EXISTS github_dispatch_deliveries_rule_id_fkey"
19312
+ ]
19313
+ }),
19314
+ defineStorageSchemaMigration({
19315
+ id: "202606050002_add_durable_plan_cleanup_tasks",
19316
+ description: "Persist retryable external cleanup tasks for side effects that cannot be completed transactionally.",
19317
+ statements: [
19318
+ `
19319
+ CREATE TABLE IF NOT EXISTS plan_cleanup_tasks (
19320
+ id uuid PRIMARY KEY,
19321
+ organization_id uuid NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
19322
+ project_id uuid NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
19323
+ cleanup_type text NOT NULL
19324
+ CHECK (cleanup_type IN ('delete_improvement_bundle_objects')),
19325
+ attempt_count integer NOT NULL DEFAULT 0,
19326
+ last_error text,
19327
+ next_attempt_at timestamptz NOT NULL DEFAULT now(),
19328
+ completed_at timestamptz,
19329
+ created_at timestamptz NOT NULL DEFAULT now(),
19330
+ updated_at timestamptz NOT NULL DEFAULT now(),
19331
+ UNIQUE (project_id, cleanup_type)
19332
+ )
19333
+ `,
19334
+ `
19335
+ CREATE INDEX IF NOT EXISTS plan_cleanup_tasks_pending_idx
19336
+ ON plan_cleanup_tasks (completed_at, next_attempt_at, created_at)
19337
+ `
19338
+ ]
19154
19339
  })
19155
19340
  ];
19156
19341
 
@@ -19902,7 +20087,7 @@ var ProjectMetricsSchema = external_exports.object({
19902
20087
  monthly_raw_ingested_events: external_exports.number().int().nonnegative(),
19903
20088
  retained_bundles: external_exports.number().int().nonnegative(),
19904
20089
  monthly_alert_deliveries: external_exports.number().int().nonnegative()
19905
- }).strict();
20090
+ });
19906
20091
  var ProjectRecordSchema = external_exports.object({
19907
20092
  project_id: external_exports.string(),
19908
20093
  organization_id: external_exports.string(),
@@ -19911,6 +20096,7 @@ var ProjectRecordSchema = external_exports.object({
19911
20096
  relationship: external_exports.enum(["owned", "shared"]),
19912
20097
  sharing_state: external_exports.enum(["private", "shared_by_you", "shared_with_you"]),
19913
20098
  effective_role: external_exports.enum(["owner", "admin", "member"]),
20099
+ shared_access_suspended: external_exports.boolean().optional(),
19914
20100
  name: external_exports.string(),
19915
20101
  slug: external_exports.string(),
19916
20102
  environment_default: external_exports.string(),
@@ -19918,13 +20104,13 @@ var ProjectRecordSchema = external_exports.object({
19918
20104
  metrics: ProjectMetricsSchema,
19919
20105
  created_at: external_exports.string(),
19920
20106
  updated_at: external_exports.string()
19921
- }).strict();
20107
+ });
19922
20108
  var ProjectListResponseSchema = external_exports.object({
19923
20109
  projects: external_exports.array(ProjectRecordSchema)
19924
- }).strict();
20110
+ });
19925
20111
  var ProjectCreateResponseSchema = external_exports.object({
19926
20112
  project: ProjectRecordSchema
19927
- }).strict();
20113
+ });
19928
20114
  var DeletedProjectRecordSchema = external_exports.object({
19929
20115
  project_id: external_exports.string(),
19930
20116
  organization_id: external_exports.string(),
@@ -19933,16 +20119,17 @@ var DeletedProjectRecordSchema = external_exports.object({
19933
20119
  relationship: external_exports.enum(["owned", "shared"]),
19934
20120
  sharing_state: external_exports.enum(["private", "shared_by_you", "shared_with_you"]),
19935
20121
  effective_role: external_exports.enum(["owner", "admin", "member"]),
20122
+ shared_access_suspended: external_exports.boolean().optional(),
19936
20123
  name: external_exports.string(),
19937
20124
  slug: external_exports.string(),
19938
20125
  environment_default: external_exports.string(),
19939
20126
  organization_plan: external_exports.enum(["free", "solo", "team"]),
19940
20127
  created_at: external_exports.string(),
19941
20128
  updated_at: external_exports.string()
19942
- }).strict();
20129
+ });
19943
20130
  var ProjectDeleteResponseSchema = external_exports.object({
19944
20131
  project: DeletedProjectRecordSchema
19945
- }).strict();
20132
+ });
19946
20133
  var ApiErrorResponseSchema2 = external_exports.object({
19947
20134
  error: external_exports.string()
19948
20135
  }).strict();
@@ -24711,6 +24898,15 @@ function collectRequestAnomalyAggregates(batches, capturePreset) {
24711
24898
  if (threshold === null || responseStatus === null || method === null || routeTemplate === null) {
24712
24899
  continue;
24713
24900
  }
24901
+ if (isLowValueExternalProbeRequestFailure404({
24902
+ httpMethod: method,
24903
+ requestPath: event.payload.path,
24904
+ routeTemplate,
24905
+ responseStatus,
24906
+ headers: event.payload.headers
24907
+ })) {
24908
+ continue;
24909
+ }
24714
24910
  const projectId = requireProjectId(event);
24715
24911
  const incidentFingerprint = buildRequestAnomalyFingerprint({
24716
24912
  projectId,
@@ -26789,6 +26985,15 @@ function formatIncidentDetail(incident) {
26789
26985
  function formatObjectOutput(payload) {
26790
26986
  return JSON.stringify(payload, null, 2);
26791
26987
  }
26988
+ function readIncidentIdsInput(input2) {
26989
+ if (Array.isArray(input2.incidentIds) && input2.incidentIds.length > 0) {
26990
+ return input2.incidentIds;
26991
+ }
26992
+ if (typeof input2.incidentId === "string" && input2.incidentId.length > 0) {
26993
+ return [input2.incidentId];
26994
+ }
26995
+ return [];
26996
+ }
26792
26997
  function formatIncidentContextDetail(context) {
26793
26998
  const incidentSource = context.incident["source"];
26794
26999
  const visibility = typeof context.visibility === "object" && context.visibility !== null && !Array.isArray(context.visibility) ? context.visibility : null;
@@ -27227,10 +27432,27 @@ async function getIncidentWithAuthCommand(input2, dependencies) {
27227
27432
  });
27228
27433
  }
27229
27434
  async function resolveIncidentCommand(input2, api) {
27435
+ const incidentIds = readIncidentIdsInput(input2);
27230
27436
  try {
27437
+ if (incidentIds.length > 1 && api.resolveIncidents !== void 0) {
27438
+ const incidents = await api.resolveIncidents({
27439
+ bearerToken: input2.bearerToken,
27440
+ incidentIds
27441
+ });
27442
+ if (input2.json) {
27443
+ return {
27444
+ exitCode: 0,
27445
+ output: JSON.stringify({ incidents })
27446
+ };
27447
+ }
27448
+ return {
27449
+ exitCode: 0,
27450
+ output: formatIncidentTable(incidents)
27451
+ };
27452
+ }
27231
27453
  const incident = await api.resolveIncident({
27232
27454
  bearerToken: input2.bearerToken,
27233
- incidentId: input2.incidentId
27455
+ incidentId: incidentIds[0]
27234
27456
  });
27235
27457
  if (input2.json) {
27236
27458
  return {
@@ -27247,12 +27469,15 @@ async function resolveIncidentCommand(input2, api) {
27247
27469
  }
27248
27470
  }
27249
27471
  async function resolveIncidentWithAuthCommand(input2, dependencies) {
27472
+ const incidentIds = readIncidentIdsInput(input2);
27250
27473
  if (await shouldUseLocalRetrieval(input2.source, dependencies)) {
27251
27474
  try {
27252
- const incident = await resolveLocalIncident({ incidentId: input2.incidentId }, dependencies);
27475
+ const incidents = await Promise.all(
27476
+ incidentIds.map((incidentId) => resolveLocalIncident({ incidentId }, dependencies))
27477
+ );
27253
27478
  return {
27254
27479
  exitCode: 0,
27255
- output: input2.json ? JSON.stringify({ incident }) : formatIncidentDetail(incident)
27480
+ output: input2.json ? JSON.stringify(incidents.length === 1 ? { incident: incidents[0] } : { incidents }) : incidents.length === 1 ? formatIncidentDetail(incidents[0]) : formatIncidentTable(incidents)
27256
27481
  };
27257
27482
  } catch (error) {
27258
27483
  return mapErrorToResult(error);
@@ -27260,24 +27485,80 @@ async function resolveIncidentWithAuthCommand(input2, dependencies) {
27260
27485
  }
27261
27486
  if (await shouldCombineLocalAndCloudRetrieval(input2.source, dependencies)) {
27262
27487
  try {
27263
- const incident = await resolveLocalIncident({ incidentId: input2.incidentId }, dependencies);
27264
- return {
27265
- exitCode: 0,
27266
- output: input2.json ? JSON.stringify({ incident }) : formatIncidentDetail(incident)
27267
- };
27488
+ const localIncidents = /* @__PURE__ */ new Map();
27489
+ const cloudIncidentIds = [];
27490
+ for (const incidentId of incidentIds) {
27491
+ try {
27492
+ localIncidents.set(incidentId, await resolveLocalIncident({ incidentId }, dependencies));
27493
+ } catch (error) {
27494
+ if (!isNotFoundRetrievalError(error)) {
27495
+ return mapErrorToResult(error);
27496
+ }
27497
+ cloudIncidentIds.push(incidentId);
27498
+ }
27499
+ }
27500
+ if (cloudIncidentIds.length === 0) {
27501
+ const incidents = incidentIds.map((incidentId) => localIncidents.get(incidentId));
27502
+ return {
27503
+ exitCode: 0,
27504
+ output: input2.json ? JSON.stringify(incidents.length === 1 ? { incident: incidents[0] } : { incidents }) : incidents.length === 1 ? formatIncidentDetail(incidents[0]) : formatIncidentTable(incidents)
27505
+ };
27506
+ }
27507
+ return runAuthenticatedCliCommand(input2, {
27508
+ createApi: createAuthenticatedRetrievalApi,
27509
+ dependencies,
27510
+ runCommand: async (authState, api) => {
27511
+ const cloudIncidents = cloudIncidentIds.length === 1 ? [
27512
+ attachSourceToRecord(
27513
+ await api.resolveIncident({
27514
+ bearerToken: authState.bearer_token,
27515
+ incidentId: cloudIncidentIds[0]
27516
+ }),
27517
+ "cloud"
27518
+ )
27519
+ ] : (await api.resolveIncidents({
27520
+ bearerToken: authState.bearer_token,
27521
+ incidentIds: cloudIncidentIds
27522
+ })).map(
27523
+ (incident) => attachSourceToRecord(incident, "cloud")
27524
+ );
27525
+ for (const incident of cloudIncidents) {
27526
+ await syncCloudIncidentCacheStatus(
27527
+ {
27528
+ incidentId: incident.incident_id,
27529
+ incident: {
27530
+ ...typeof incident.status === "string" ? { status: incident.status } : {},
27531
+ resolved_at: incident.resolved_at ?? null
27532
+ }
27533
+ },
27534
+ dependencies
27535
+ );
27536
+ localIncidents.set(incident.incident_id, incident);
27537
+ }
27538
+ const incidents = incidentIds.map((incidentId) => localIncidents.get(incidentId));
27539
+ return {
27540
+ exitCode: 0,
27541
+ output: input2.json ? JSON.stringify(incidents.length === 1 ? { incident: incidents[0] } : { incidents }) : incidents.length === 1 ? formatIncidentDetail(incidents[0]) : formatIncidentTable(incidents)
27542
+ };
27543
+ }
27544
+ });
27268
27545
  } catch (error) {
27269
- if (!isNotFoundRetrievalError(error)) {
27546
+ if (!(error instanceof Error)) {
27270
27547
  return mapErrorToResult(error);
27271
27548
  }
27549
+ return mapErrorToResult(error);
27272
27550
  }
27273
27551
  }
27274
27552
  return runAuthenticatedCliCommand(input2, {
27275
27553
  createApi: createAuthenticatedRetrievalApi,
27276
27554
  dependencies,
27277
27555
  runCommand: (authState, api) => {
27278
- const commandInput = {
27556
+ const commandInput = incidentIds.length === 1 ? {
27279
27557
  bearerToken: authState.bearer_token,
27280
- incidentId: input2.incidentId
27558
+ incidentId: incidentIds[0]
27559
+ } : {
27560
+ bearerToken: authState.bearer_token,
27561
+ incidentIds
27281
27562
  };
27282
27563
  if (input2.json !== void 0) {
27283
27564
  commandInput.json = input2.json;
@@ -27290,7 +27571,7 @@ async function resolveIncidentWithAuthCommand(input2, dependencies) {
27290
27571
  );
27291
27572
  await syncCloudIncidentCacheStatus(
27292
27573
  {
27293
- incidentId: input2.incidentId,
27574
+ incidentId: incident.incident_id,
27294
27575
  incident: {
27295
27576
  ...typeof incident.status === "string" ? { status: incident.status } : {},
27296
27577
  resolved_at: incident.resolved_at ?? null
@@ -27299,19 +27580,54 @@ async function resolveIncidentWithAuthCommand(input2, dependencies) {
27299
27580
  dependencies
27300
27581
  );
27301
27582
  return incident;
27583
+ },
27584
+ resolveIncidents: async (requestInput) => {
27585
+ const incidents = (await api.resolveIncidents(requestInput)).map(
27586
+ (incident) => attachSourceToRecord(incident, "cloud")
27587
+ );
27588
+ for (const incident of incidents) {
27589
+ await syncCloudIncidentCacheStatus(
27590
+ {
27591
+ incidentId: incident.incident_id,
27592
+ incident: {
27593
+ ...typeof incident.status === "string" ? { status: incident.status } : {},
27594
+ resolved_at: incident.resolved_at ?? null
27595
+ }
27596
+ },
27597
+ dependencies
27598
+ );
27599
+ }
27600
+ return incidents;
27302
27601
  }
27303
27602
  });
27304
27603
  }
27305
27604
  });
27306
27605
  }
27307
27606
  async function reopenIncidentCommand(input2, api) {
27607
+ const incidentIds = readIncidentIdsInput(input2);
27308
27608
  if (api.reopenIncident === void 0) {
27309
27609
  return mapUnsupportedReopenResult();
27310
27610
  }
27311
27611
  try {
27612
+ if (incidentIds.length > 1 && api.reopenIncidents !== void 0) {
27613
+ const incidents = await api.reopenIncidents({
27614
+ bearerToken: input2.bearerToken,
27615
+ incidentIds
27616
+ });
27617
+ if (input2.json) {
27618
+ return {
27619
+ exitCode: 0,
27620
+ output: JSON.stringify({ incidents })
27621
+ };
27622
+ }
27623
+ return {
27624
+ exitCode: 0,
27625
+ output: formatIncidentTable(incidents)
27626
+ };
27627
+ }
27312
27628
  const incident = await api.reopenIncident({
27313
27629
  bearerToken: input2.bearerToken,
27314
- incidentId: input2.incidentId
27630
+ incidentId: incidentIds[0]
27315
27631
  });
27316
27632
  if (input2.json) {
27317
27633
  return {
@@ -27328,12 +27644,15 @@ async function reopenIncidentCommand(input2, api) {
27328
27644
  }
27329
27645
  }
27330
27646
  async function reopenIncidentWithAuthCommand(input2, dependencies) {
27647
+ const incidentIds = readIncidentIdsInput(input2);
27331
27648
  if (await shouldUseLocalRetrieval(input2.source, dependencies)) {
27332
27649
  try {
27333
- const incident = await reopenLocalIncident({ incidentId: input2.incidentId }, dependencies);
27650
+ const incidents = await Promise.all(
27651
+ incidentIds.map((incidentId) => reopenLocalIncident({ incidentId }, dependencies))
27652
+ );
27334
27653
  return {
27335
27654
  exitCode: 0,
27336
- output: input2.json ? JSON.stringify({ incident }) : formatIncidentDetail(incident)
27655
+ output: input2.json ? JSON.stringify(incidents.length === 1 ? { incident: incidents[0] } : { incidents }) : incidents.length === 1 ? formatIncidentDetail(incidents[0]) : formatIncidentTable(incidents)
27337
27656
  };
27338
27657
  } catch (error) {
27339
27658
  return mapErrorToResult(error);
@@ -27341,24 +27660,80 @@ async function reopenIncidentWithAuthCommand(input2, dependencies) {
27341
27660
  }
27342
27661
  if (await shouldCombineLocalAndCloudRetrieval(input2.source, dependencies)) {
27343
27662
  try {
27344
- const incident = await reopenLocalIncident({ incidentId: input2.incidentId }, dependencies);
27345
- return {
27346
- exitCode: 0,
27347
- output: input2.json ? JSON.stringify({ incident }) : formatIncidentDetail(incident)
27348
- };
27663
+ const localIncidents = /* @__PURE__ */ new Map();
27664
+ const cloudIncidentIds = [];
27665
+ for (const incidentId of incidentIds) {
27666
+ try {
27667
+ localIncidents.set(incidentId, await reopenLocalIncident({ incidentId }, dependencies));
27668
+ } catch (error) {
27669
+ if (!isNotFoundRetrievalError(error)) {
27670
+ return mapErrorToResult(error);
27671
+ }
27672
+ cloudIncidentIds.push(incidentId);
27673
+ }
27674
+ }
27675
+ if (cloudIncidentIds.length === 0) {
27676
+ const incidents = incidentIds.map((incidentId) => localIncidents.get(incidentId));
27677
+ return {
27678
+ exitCode: 0,
27679
+ output: input2.json ? JSON.stringify(incidents.length === 1 ? { incident: incidents[0] } : { incidents }) : incidents.length === 1 ? formatIncidentDetail(incidents[0]) : formatIncidentTable(incidents)
27680
+ };
27681
+ }
27682
+ return runAuthenticatedCliCommand(input2, {
27683
+ createApi: createAuthenticatedRetrievalApi,
27684
+ dependencies,
27685
+ runCommand: async (authState, api) => {
27686
+ const cloudIncidents = cloudIncidentIds.length === 1 ? [
27687
+ attachSourceToRecord(
27688
+ await api.reopenIncident({
27689
+ bearerToken: authState.bearer_token,
27690
+ incidentId: cloudIncidentIds[0]
27691
+ }),
27692
+ "cloud"
27693
+ )
27694
+ ] : (await api.reopenIncidents({
27695
+ bearerToken: authState.bearer_token,
27696
+ incidentIds: cloudIncidentIds
27697
+ })).map(
27698
+ (incident) => attachSourceToRecord(incident, "cloud")
27699
+ );
27700
+ for (const incident of cloudIncidents) {
27701
+ await syncCloudIncidentCacheStatus(
27702
+ {
27703
+ incidentId: incident.incident_id,
27704
+ incident: {
27705
+ ...typeof incident.status === "string" ? { status: incident.status } : {},
27706
+ resolved_at: null
27707
+ }
27708
+ },
27709
+ dependencies
27710
+ );
27711
+ localIncidents.set(incident.incident_id, incident);
27712
+ }
27713
+ const incidents = incidentIds.map((incidentId) => localIncidents.get(incidentId));
27714
+ return {
27715
+ exitCode: 0,
27716
+ output: input2.json ? JSON.stringify(incidents.length === 1 ? { incident: incidents[0] } : { incidents }) : incidents.length === 1 ? formatIncidentDetail(incidents[0]) : formatIncidentTable(incidents)
27717
+ };
27718
+ }
27719
+ });
27349
27720
  } catch (error) {
27350
- if (!isNotFoundRetrievalError(error)) {
27721
+ if (!(error instanceof Error)) {
27351
27722
  return mapErrorToResult(error);
27352
27723
  }
27724
+ return mapErrorToResult(error);
27353
27725
  }
27354
27726
  }
27355
27727
  return runAuthenticatedCliCommand(input2, {
27356
27728
  createApi: createAuthenticatedRetrievalApi,
27357
27729
  dependencies,
27358
27730
  runCommand: (authState, api) => {
27359
- const commandInput = {
27731
+ const commandInput = incidentIds.length === 1 ? {
27360
27732
  bearerToken: authState.bearer_token,
27361
- incidentId: input2.incidentId
27733
+ incidentId: incidentIds[0]
27734
+ } : {
27735
+ bearerToken: authState.bearer_token,
27736
+ incidentIds
27362
27737
  };
27363
27738
  if (input2.json !== void 0) {
27364
27739
  commandInput.json = input2.json;
@@ -27371,7 +27746,7 @@ async function reopenIncidentWithAuthCommand(input2, dependencies) {
27371
27746
  );
27372
27747
  await syncCloudIncidentCacheStatus(
27373
27748
  {
27374
- incidentId: input2.incidentId,
27749
+ incidentId: incident.incident_id,
27375
27750
  incident: {
27376
27751
  ...typeof incident.status === "string" ? { status: incident.status } : {},
27377
27752
  resolved_at: null
@@ -27380,6 +27755,24 @@ async function reopenIncidentWithAuthCommand(input2, dependencies) {
27380
27755
  dependencies
27381
27756
  );
27382
27757
  return incident;
27758
+ },
27759
+ reopenIncidents: async (requestInput) => {
27760
+ const incidents = (await api.reopenIncidents(requestInput)).map(
27761
+ (incident) => attachSourceToRecord(incident, "cloud")
27762
+ );
27763
+ for (const incident of incidents) {
27764
+ await syncCloudIncidentCacheStatus(
27765
+ {
27766
+ incidentId: incident.incident_id,
27767
+ incident: {
27768
+ ...typeof incident.status === "string" ? { status: incident.status } : {},
27769
+ resolved_at: null
27770
+ }
27771
+ },
27772
+ dependencies
27773
+ );
27774
+ }
27775
+ return incidents;
27383
27776
  }
27384
27777
  });
27385
27778
  }
@@ -28591,8 +28984,8 @@ var CLI_USAGE_LINES = [
28591
28984
  " debugbundle incidents [--source <local|cloud>] [--project-id <id>] [--environment <name>] [--service <name>] [--status <status>] [--severity <severity>] [--cursor <cursor>] [--limit <n>] [--auth-file <path>] [--json]",
28592
28985
  " debugbundle inspect <incident-id> [--source <local|cloud>] [--auth-file <path>] [--json]",
28593
28986
  " debugbundle explain <incident-id> [--source <local|cloud>] [--auth-file <path>] [--json]",
28594
- " debugbundle resolve <incident-id> [--source <local|cloud>] [--auth-file <path>] [--json]",
28595
- " debugbundle reopen <incident-id> [--source <local|cloud>] [--auth-file <path>] [--json]",
28987
+ " debugbundle resolve <incident-id> [incident-id ...] [--source <local|cloud>] [--auth-file <path>] [--json]",
28988
+ " debugbundle reopen <incident-id> [incident-id ...] [--source <local|cloud>] [--auth-file <path>] [--json]",
28596
28989
  " debugbundle bundle <incident-id> [--source <local|cloud>] [--auth-file <path>] [--json]",
28597
28990
  " debugbundle reproduce <incident-id> [--source <local|cloud>] [--auth-file <path>] [--json]",
28598
28991
  " debugbundle logs <incident-id> [--level <level>] [--cursor <cursor>] [--limit <n>] [--auth-file <path>] [--json]",
@@ -34193,7 +34586,7 @@ async function handleCaptureRuleCommand2(parsedArgv, dependencies) {
34193
34586
  // package.json
34194
34587
  var package_default = {
34195
34588
  name: "@debugbundle/cli",
34196
- version: "1.1.0",
34589
+ version: "1.1.2",
34197
34590
  private: false,
34198
34591
  description: "Command-line interface for DebugBundle",
34199
34592
  license: "AGPL-3.0-only",
@@ -34547,9 +34940,11 @@ ${formatUsage()}`
34547
34940
  }
34548
34941
  if (command === "resolve") {
34549
34942
  expectNoUnknownOptions(parsedArgv, ["auth-file", "json", "source"]);
34550
- ensureNoExtraPositionals(parsedArgv, 2);
34943
+ if (parsedArgv.positionals.length < 2) {
34944
+ throw new CliInputError("Missing required positional argument incident-id.");
34945
+ }
34551
34946
  const input2 = appendCommonAuthOptions(parsedArgv, {
34552
- incidentId: requirePositional(parsedArgv, 1, "incident-id")
34947
+ incidentIds: parsedArgv.positionals.slice(1)
34553
34948
  });
34554
34949
  const source = readRetrievalSource(parsedArgv);
34555
34950
  if (source !== void 0) {
@@ -34559,9 +34954,11 @@ ${formatUsage()}`
34559
34954
  }
34560
34955
  if (command === "reopen") {
34561
34956
  expectNoUnknownOptions(parsedArgv, ["auth-file", "json", "source"]);
34562
- ensureNoExtraPositionals(parsedArgv, 2);
34957
+ if (parsedArgv.positionals.length < 2) {
34958
+ throw new CliInputError("Missing required positional argument incident-id.");
34959
+ }
34563
34960
  const input2 = appendCommonAuthOptions(parsedArgv, {
34564
- incidentId: requirePositional(parsedArgv, 1, "incident-id")
34961
+ incidentIds: parsedArgv.positionals.slice(1)
34565
34962
  });
34566
34963
  const source = readRetrievalSource(parsedArgv);
34567
34964
  if (source !== void 0) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@debugbundle/cli",
3
- "version": "1.1.0",
3
+ "version": "1.1.2",
4
4
  "private": false,
5
5
  "description": "Command-line interface for DebugBundle",
6
6
  "license": "AGPL-3.0-only",