@chainpatrol/cli 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,35 @@
1
1
  # @chainpatrol/cli
2
2
 
3
+ ## 0.5.0
4
+
5
+ ### Minor Changes
6
+
7
+ - f8d9454: Add five new healthchecks covering the takedown pipeline and asset liveness, expanding the runnable surface for `chainpatrol healthchecks run`:
8
+
9
+ - **`takedowns.todo-volume`** — counts takedowns sitting in TODO. Default warn=50, fail=100. Catches automation gaps on new threat surfaces or manual-filing capacity issues.
10
+ - **`takedowns.in-progress-volume`** — counts takedowns currently IN*PROGRESS regardless of age. Default warn=30, fail=75. Complements `takedowns.stale-in-progress` (age-based) by catching submission-format / vendor-side issues \_before* items go stale.
11
+ - **`takedowns.cancelled-count`** — counts transitions into CANCELLED from the `TakedownEvent` log over a rolling window. Default 7d / warn=3 / fail=10. Replaces the placeholder `takedowns.cancelled-rate` registry entry. Uses the event log rather than `Takedown.updatedAt` so the cancellation date is correct even if the takedown has since been edited.
12
+ - **`takedowns.automation-off`** — flags orgs with takedown service enabled but `isAutomatedTakedownsActive` off for too long. Default warn=30d, fail=60d. Age is derived from the most recent `SERVICES_AUTOMATED_TAKEDOWNS_UPDATED` `OrganizationEvent`, falling back to `Organization.updatedAt`. Orgs with takedown service entirely disabled are skipped.
13
+ - **`assets.dead-asset-spike`** — compares `DETECTED_AS_DEAD` events in the current window against the prior baseline rate; warns on a multiplier exceeding the threshold (default 24h vs 7d, ×2 warn / ×4 fail) once the current count clears the `minSpikeCount` floor. Catches liveness-checker regressions after platform changes.
14
+
15
+ The bundled CLI skill gets a rewritten **Takedowns** section covering all four takedown checks and a new top-level **Assets** section covering `dead-asset-spike` plus guidance on the still-unimplemented `assets.dead-but-alive` / `assets.alive-but-marked-dead` (which require live HTTP probes and are not a fit for synchronous healthchecks).
16
+
17
+ The public `HealthcheckResult.category` enum gains `"assets"`. `observed` and `threshold` records now also accept booleans and null (for fields like `automatedTakedownsActive` and the nullable `ratio` on the spike check).
18
+
19
+ `HealthcheckResult` also gains an `appUrl` field (string or null) that deep-links to the relevant filtered admin page in the web app. For example, `takedowns.stale-in-progress` returns `https://app.chainpatrol.io/admin/<slug>/takedowns?takedownStatus=IN_PROGRESS&sortBy=oldest` and `reviewing.backlog` returns `https://app.chainpatrol.io/admin/<slug>/review?excludeWatchlisted=true`. The CLI prints `View in app: <url>` after each result. Checks without a sensible filtered view (`detections.silent-configs`, `assets.dead-asset-spike`) return `null`. The base URL respects `BETTER_AUTH_URL` and falls back to `https://app.chainpatrol.io`.
20
+
21
+ ## 0.4.1
22
+
23
+ ### Patch Changes
24
+
25
+ - 9126b45: Split the `reviewing.backlog` healthcheck into reviewable vs. total pending so the count matches the reviewing page in the app. The endpoint now reports `pendingProposals` (total), `reviewableProposals` (the subset the UI shows by default — assets not watchlisted, or reports from a customer), and `watchlistedHiddenProposals` (delta). Severity grades on the reviewable count; a non-zero watchlisted bucket surfaces as a warn-level finding. The bundled CLI skill is updated to document the new shape.
26
+ - fb0b846: Split the reviewing healthchecks along the operational divide between "Needs Review" (high-priority queue reviewers act on) and "Watchlisted" (intentionally deferred bucket):
27
+
28
+ - `reviewing.backlog` and `reviewing.old-proposals` now grade the Needs-Review bucket only — assets not on a watchlist, or reports submitted by a customer. Their counts now match what the reviewing page in the app shows by default.
29
+ - Two new checks cover the Watchlisted bucket as separate first-class signals: `reviewing.watchlist-backlog` (pile-up of watchlisted pending proposals) and `reviewing.watchlist-old` (watchlisted proposals deferred too long). Severity for both is **capped at warn** — watchlist cleanup matters but should never block on the same SLA as Needs Review.
30
+
31
+ The CLI skill is updated to spell out the distinction so a healthcheck report groups findings under the right bucket.
32
+
3
33
  ## 0.4.0
4
34
 
5
35
  ### Minor Changes
@@ -321,20 +321,76 @@ chainpatrol --json healthchecks run reviewing.backlog --org <slug>
321
321
  chainpatrol --json healthchecks run --all --org <slug>
322
322
  \`\`\`
323
323
 
324
- Each implemented check has a stable id of the form \`category.name\`, e.g.
325
- \`detections.silent-configs\`, \`reviewing.backlog\`,
326
- \`reviewing.old-proposals\`, \`takedowns.stale-in-progress\`.
324
+ Each implemented check has a stable id of the form \`category.name\`. Implemented
325
+ ids today: \`detections.silent-configs\`, \`reviewing.backlog\`,
326
+ \`reviewing.old-proposals\`, \`reviewing.watchlist-backlog\`,
327
+ \`reviewing.watchlist-old\`, \`takedowns.todo-volume\`,
328
+ \`takedowns.in-progress-volume\`, \`takedowns.stale-in-progress\`,
329
+ \`takedowns.cancelled-count\`, \`takedowns.automation-off\`,
330
+ \`assets.dead-asset-spike\`.
331
+
332
+ ### Pending proposals: "Needs Review" vs "Watchlisted"
333
+
334
+ PENDING proposals split into two operationally distinct buckets, and we
335
+ grade them with separate checks:
336
+
337
+ - **Needs Review** \u2014 pending proposals the reviewing UI shows by default
338
+ (\`excludeWatchlisted=true\`): assets that are NOT on a watchlist, OR
339
+ reports submitted by a customer (those stay visible even when the asset
340
+ is watchlisted). This is the actionable queue reviewers work from, so
341
+ pile-ups and old items here are high-priority signals (\`fail\` severity
342
+ is reachable).
343
+ - **Watchlisted** \u2014 pending proposals on watchlisted assets (excluding
344
+ customer-reported reports). The reviewing UI hides these by default
345
+ because watchlisting is the act of intentionally deferring an asset.
346
+ Pile-ups and aged items here are worth surfacing as cleanup work, but
347
+ severity is **capped at warn** so they never block on the same SLA as
348
+ Needs Review. When reporting findings, treat these as lower-priority.
349
+
350
+ Each healthcheck result includes an \`appUrl\` field (string or null) that
351
+ deep-links to the relevant filtered admin page in the web app \u2014 e.g. the
352
+ takedowns page filtered to IN_PROGRESS for \`takedowns.stale-in-progress\`,
353
+ or the review page filtered to oldest pending for \`reviewing.old-proposals\`.
354
+ **When reporting a non-OK healthcheck to the user, always surface the
355
+ \`appUrl\` so they can jump straight to the right view.** Some checks
356
+ (\`detections.silent-configs\`, \`assets.dead-asset-spike\`) emit \`null\`
357
+ because no filterable list page exists for that signal yet.
327
358
 
328
359
  Implemented checks today:
329
360
 
330
361
  - **detections.silent-configs** \u2014 equivalent to \`detections healthcheck\`,
331
362
  exposed under the uniform shape.
332
- - **reviewing.backlog** \u2014 counts proposals in PENDING review state and grades
363
+ - **reviewing.backlog** \u2014 counts Needs Review pending proposals and grades
333
364
  severity against per-org thresholds (default warn=50, fail=100).
334
- - **reviewing.old-proposals** \u2014 counts proposals older than the warn / fail
335
- age thresholds (default 7 / 14 days) and lists the oldest offenders.
365
+ - **reviewing.old-proposals** \u2014 counts Needs Review proposals older than
366
+ the warn / fail age thresholds (default 7 / 14 days) and lists the
367
+ oldest offenders.
368
+ - **reviewing.watchlist-backlog** \u2014 counts watchlisted pending proposals
369
+ (default warn=200). Severity capped at warn.
370
+ - **reviewing.watchlist-old** \u2014 counts watchlisted pending proposals older
371
+ than the warn-age threshold (default 30 days) and lists the oldest.
372
+ Severity capped at warn.
373
+ - **takedowns.todo-volume** \u2014 counts takedowns in TODO (default warn=50,
374
+ fail=100). Pile-ups here usually mean an automation gap on a new threat
375
+ surface, or manual-filing capacity issues.
376
+ - **takedowns.in-progress-volume** \u2014 counts takedowns currently IN_PROGRESS
377
+ regardless of age (default warn=30, fail=75). Complements
378
+ \`stale-in-progress\` \u2014 a high count signals vendor-side or
379
+ submission-format problems even before items go stale.
336
380
  - **takedowns.stale-in-progress** \u2014 counts takedowns sitting in IN_PROGRESS
337
381
  past the staleness threshold (default 7 days) and lists the oldest.
382
+ - **takedowns.cancelled-count** \u2014 counts CANCELLED transitions from the
383
+ TakedownEvent log over a rolling window (default 7d, warn=3, fail=10).
384
+ Cancellations should be rare; a spike usually means a proposal-funnel
385
+ quality problem or misuse of the CANCELLED status.
386
+ - **takedowns.automation-off** \u2014 flags orgs with takedown service enabled
387
+ but \`isAutomatedTakedownsActive\` off for too long (default warn=30d,
388
+ fail=60d). Skipped for orgs with takedown service entirely disabled.
389
+ - **assets.dead-asset-spike** \u2014 compares DEAD-detection events in the
390
+ current window against the prior baseline rate; warns on a multiplier
391
+ exceeding the threshold (default 24h vs 7d, \xD72 warn / \xD74 fail) once the
392
+ current count clears the \`minSpikeCount\` floor. Catches liveness-checker
393
+ regressions after platform changes.
338
394
 
339
395
  The following checks are listed by \`healthchecks list\` (\`implemented: false\`)
340
396
  but **not yet implemented on the backend** \u2014 when the agent surfaces them in
@@ -348,8 +404,11 @@ a healthcheck report, mark them explicitly as "manual check, no API yet":
348
404
  human approvers in the review history. Use \`metrics breakdown\` as a proxy.
349
405
  - **blocklisting.gsb-cancelled-rate** \u2014 Google Safe Browsing submission state
350
406
  is not yet exposed in the public API.
351
- - **takedowns.todo-volume** / **takedowns.cancelled-rate** /
352
- **takedowns.automation-off** \u2014 the underlying read paths are not yet exposed.
407
+ - **assets.dead-but-alive** / **assets.alive-but-marked-dead** \u2014 require live
408
+ HTTP probes against asset URLs, which is not a synchronous-healthcheck
409
+ shape. Until a dedicated probe command exists, sample a handful manually
410
+ from \`assets list --status DEAD\` (or ALIVE) and verify in a browser. Notify
411
+ ChainPatrol engineering if the liveness checker looks miscalibrated.
353
412
 
354
413
  When the user asks to "run a healthcheck on org X", the canonical command is:
355
414
 
@@ -644,7 +703,23 @@ executed. For soft drops (still producing results but below baseline),
644
703
 
645
704
  ### Reviewing
646
705
 
647
- #### Pile Up / Backlog of Unreviewed Proposals
706
+ PENDING proposals split into two operationally distinct buckets:
707
+
708
+ - **Needs Review** \u2014 assets not on a watchlist, or reports submitted by a
709
+ customer. This is the reviewing UI's default view (\`excludeWatchlisted=true\`)
710
+ and the actionable queue reviewers work from. Pile-ups and aged items
711
+ here are high priority \u2014 \`fail\` severity is reachable.
712
+ - **Watchlisted** \u2014 pending proposals on watchlisted assets (excluding
713
+ customer-reported reports). The UI hides these by default because
714
+ watchlisting is the act of intentionally deferring the asset. Pile-ups
715
+ and aged items here are worth surfacing as cleanup work but **severity
716
+ is capped at warn** \u2014 they should never block on the same SLA as Needs
717
+ Review.
718
+
719
+ Each bucket has its own pile-up and age check, so you can grade them
720
+ independently and tune thresholds without one drowning the other.
721
+
722
+ #### Pile Up / Backlog of Needs-Review Proposals
648
723
 
649
724
  Too many proposals waiting in review. For most organizations this is over 100
650
725
  reports, but really the threshold is relative to the average number of
@@ -652,30 +727,50 @@ confirmed threats per week. Example: if an org only adds 5 blocked threats per
652
727
  week, then a 7-day backlog of even 10 proposals is a really big deal.
653
728
 
654
729
  **Run via CLI:** **Implemented as \`healthchecks run reviewing.backlog\`.**
655
- The endpoint counts proposals in PENDING review state and grades severity
730
+ The endpoint counts the **Needs Review** subset only (assets not
731
+ watchlisted, or reports marked \`reportedByCustomer\`) and grades severity
656
732
  against per-org thresholds (default warn=50, fail=100; override with
657
- \`--warn-threshold\` / \`--fail-threshold\` via the run payload). For raw
658
- counts plus SLA / age breakdowns, \`chainpatrol --json queues snapshot --org <slug>\`
659
- remains useful and exposes \`reviewQueue.totalPendingProposals\` and
660
- \`reviewQueue.distinctReports\`. Compare against the org's typical weekly
661
- throughput (use \`metrics summary --this-week\` for that baseline) \u2014 a
662
- backlog that exceeds a week of typical confirmed-threat volume is a finding
663
- regardless of the absolute number.
664
-
665
- #### Really Old Proposals
666
-
667
- Any proposal waiting for review longer than 14 days is a sign something has
668
- gone wrong. Even complex investigations rarely take longer than this. Except
669
- for rare cases, these should be rejected or approved to prevent a backlog
670
- from building.
733
+ \`--warn-threshold\` / \`--fail-threshold\` via the run payload). This is
734
+ the number the reviewing page in the app shows by default, so the
735
+ healthcheck output matches what reviewers see.
736
+
737
+ For raw counts plus SLA / age breakdowns,
738
+ \`chainpatrol --json queues snapshot --org <slug>\` remains useful and
739
+ exposes \`reviewQueue.totalPendingProposals\` and
740
+ \`reviewQueue.distinctReports\` (note: \`queues snapshot\` does NOT apply
741
+ the watchlist filter, so its number is the sum of Needs Review +
742
+ Watchlisted). Compare against the org's typical weekly throughput
743
+ (use \`metrics summary --this-week\` for that baseline) \u2014 a backlog that
744
+ exceeds a week of typical confirmed-threat volume is a finding regardless
745
+ of the absolute number.
746
+
747
+ #### Really Old Needs-Review Proposals
748
+
749
+ Any Needs-Review proposal waiting longer than 14 days is a sign something
750
+ has gone wrong. Even complex investigations rarely take longer than this.
751
+ Except for rare cases, these should be rejected or approved to prevent a
752
+ backlog from building.
671
753
 
672
754
  **Run via CLI:** **Implemented as \`healthchecks run reviewing.old-proposals\`.**
673
- The endpoint counts pending proposals older than the warn / fail age
755
+ The endpoint counts Needs-Review proposals older than the warn / fail age
674
756
  thresholds (default 7 / 14 days) and lists the oldest offenders in
675
757
  \`findings\`. \`queues snapshot\` (\`reviewQueue.ageBuckets.gte168h\`) still
676
758
  works as a raw view, and \`reviewQueue.slaBuckets.breached\` captures the
677
759
  strictest SLA breaches separately \u2014 any non-zero value is worth raising.
678
760
 
761
+ #### Watchlist Pile-Up / Old Watchlisted Proposals
762
+
763
+ Watchlisted-pending proposals are deferred on purpose, but they shouldn't
764
+ grow unbounded \u2014 a huge pile or very-old items signal that the watchlist
765
+ needs a cleanup pass. These don't block on the same SLA as Needs Review.
766
+
767
+ **Run via CLI:** **Implemented as \`healthchecks run reviewing.watchlist-backlog\`**
768
+ (pile-up count, default warn=200) and
769
+ **\`healthchecks run reviewing.watchlist-old\`** (default warn-age 30
770
+ days). Both cap severity at warn. When reporting findings during an org
771
+ healthcheck, group them under "watchlist cleanup" rather than mixing with
772
+ Needs-Review findings \u2014 they're operationally different concerns.
773
+
679
774
  #### Spike in Auto Approved Reports
680
775
 
681
776
  If suddenly a lot of proposals in an org are being approved by automation,
@@ -714,19 +809,23 @@ exposed in the public API, this remains a manual / engineering-team check.
714
809
 
715
810
  ### Takedowns
716
811
 
812
+ The takedown pipeline has three stages \u2014 TODO (queued, not yet filed),
813
+ IN_PROGRESS (filed, waiting on vendor / customer / refile), and a terminal
814
+ state (COMPLETED or CANCELLED). Healthchecks cover pile-ups at each stage,
815
+ plus quality/configuration issues.
816
+
717
817
  #### Too Many Takedowns in ToDo
718
818
 
719
819
  Can mean a gap in automated takedowns not being implemented for some new area
720
820
  of threats. It can also mean the areas that require manual takedowns are
721
821
  being missed by the takedown team.
722
822
 
723
- **Run via CLI:** **Not yet implemented as a healthcheck endpoint.** Listed
724
- in \`healthchecks list\` as \`takedowns.todo-volume\` with
725
- \`implemented: false\` (needs a per-org throughput baseline). As an interim
726
- signal, \`chainpatrol --json queues snapshot --org <slug>\` returns
727
- \`takedownQueue.totalOpen\` and breakdowns by status; a persistently large
728
- ToDo bucket relative to the org's typical takedown rate (use
729
- \`metrics summary\` for that baseline) is the finding.
823
+ **Run via CLI:** **Implemented as \`healthchecks run takedowns.todo-volume\`.**
824
+ Counts takedowns sitting in TODO (default warn=50, fail=100). For raw
825
+ breakdowns by type, cross-reference with
826
+ \`chainpatrol --json metrics breakdown --org <slug> --by assetType\` \u2014
827
+ items piled up on a specific platform usually point at an automation
828
+ gap there.
730
829
 
731
830
  #### Too Many Takedowns In Progress
732
831
 
@@ -734,12 +833,16 @@ Typically means something is wrong with the submission itself. The takedown
734
833
  may need to be resubmitted, the vendor asked for more evidence, or we may
735
834
  have submitted it in the wrong place.
736
835
 
737
- **Run via CLI:** **Implemented as \`healthchecks run takedowns.stale-in-progress\`.**
738
- The endpoint counts takedowns sitting in IN_PROGRESS past the staleness
739
- threshold (default 7 days) and lists the oldest offenders in \`findings\`.
740
- \`queues snapshot\` (\`takedownQueue.staleInProgress\`) still works as a raw
741
- view. Any non-zero value is worth investigating; a growing number across
742
- snapshots strongly suggests a vendor-side or submission-format problem.
836
+ **Run via CLI:** Two checks, complementary:
837
+
838
+ - **\`healthchecks run takedowns.in-progress-volume\`** \u2014 counts all
839
+ IN_PROGRESS takedowns regardless of age (default warn=30, fail=75).
840
+ Catches a vendor-side or submission-format problem before items go
841
+ stale.
842
+ - **\`healthchecks run takedowns.stale-in-progress\`** \u2014 counts IN_PROGRESS
843
+ takedowns past a staleness threshold (default 7 days), lists the oldest
844
+ offenders. Any non-zero value is worth investigating; a growing count
845
+ across snapshots strongly suggests vendor-side or format issues.
743
846
 
744
847
  #### Too Many Cancelled Takedowns
745
848
 
@@ -748,14 +851,13 @@ do this takedown" for some reason. Cases like adding an item to the blocklist
748
851
  when it's already taken down are treated as completed, not cancelled. So even
749
852
  3 cancelled takedowns in a 7-day period is too many.
750
853
 
751
- **Run via CLI:** **Not yet implemented as a healthcheck endpoint.** Listed
752
- in \`healthchecks list\` as \`takedowns.cancelled-rate\` with
753
- \`implemented: false\` (public API does not yet expose CANCELLED takedown
754
- counts over a rolling window). As an interim signal, use
755
- \`chainpatrol --json metrics breakdown --org <slug> --by day --this-week\`
756
- and compare \`takedownsFiled\` vs \`takedownsCompleted\` for a sudden
757
- divergence \u2014 a widening gap with no In-Progress growth often shows up as
758
- cancellations.
854
+ **Run via CLI:** **Implemented as \`healthchecks run takedowns.cancelled-count\`.**
855
+ Counts transitions into the CANCELLED status from the TakedownEvent log
856
+ within the lookback window (default 7 days, warn=3, fail=10). The check
857
+ uses the event log rather than \`Takedown.updatedAt\` so it correctly
858
+ attributes the cancellation date even if the takedown has since been
859
+ edited. A spike usually means a quality problem in the proposal funnel
860
+ or the CANCELLED status being used as a catch-all.
759
861
 
760
862
  #### Automated Takedowns Turned Off for Over 30 Days
761
863
 
@@ -763,12 +865,56 @@ Automated takedowns should be on by default for nearly every organization.
763
865
  Any issue that would make you want to turn off automated takedowns should be
764
866
  resolved within 30 days.
765
867
 
766
- **Run via CLI:** **Not yet implemented as a healthcheck endpoint.** Listed
767
- in \`healthchecks list\` as \`takedowns.automation-off\` with
768
- \`implemented: false\` (public API does not yet expose automated-takedown
769
- enablement state). Check this manually with the org's takedown automation
770
- settings; the CLI's role here is mostly to highlight stale state in
771
- \`queues snapshot\` so you know to ask.
868
+ **Run via CLI:** **Implemented as \`healthchecks run takedowns.automation-off\`.**
869
+ Checks \`Organization.isAutomatedTakedownsActive\` and derives the
870
+ off-duration from the most recent \`SERVICES_AUTOMATED_TAKEDOWNS_UPDATED\`
871
+ entry in \`OrganizationEvent\` (default warn 30 days, fail 60 days). Orgs
872
+ with takedown service entirely disabled (\`isTakedownsActive=0\`) are
873
+ skipped \u2014 automation being off is implied in that case.
874
+
875
+ ### Assets
876
+
877
+ Healthchecks on the asset model \u2014 specifically asset liveness state, which
878
+ the takedown team depends on for follow-up.
879
+
880
+ #### Spike in Recently Dead Assets
881
+
882
+ A sudden spike in DEAD detections can be a good signal (takedowns or platform
883
+ moderation working) but it can also mean the liveness checker is
884
+ misclassifying assets after a platform change, captcha rollout, or anti-bot
885
+ update. If many assets become dead at once, sample a few manually.
886
+
887
+ **Run via CLI:** **Implemented as \`healthchecks run assets.dead-asset-spike\`.**
888
+ Compares \`DETECTED_AS_DEAD\` events in the current window (default 24h)
889
+ against the baseline rate from the prior \`baselineDays\` (default 7d).
890
+ Severity fires only when the current count clears \`minSpikeCount\` (default
891
+ 10) AND exceeds the multiplier (default warn \xD72, fail \xD74). The
892
+ \`minSpikeCount\` floor suppresses noise on orgs with near-zero baseline
893
+ activity. When this fires, pull a sample of recent DEAD assets, verify a
894
+ few in a browser, and notify ChainPatrol engineering if the sample is
895
+ clearly still live.
896
+
897
+ #### Assets Marked Dead but Still Online / Assets Not Marked Dead Even Though They Are Down
898
+
899
+ These two opposite failure modes are **not implemented as healthchecks**
900
+ (listed as \`assets.dead-but-alive\` and \`assets.alive-but-marked-dead\` with
901
+ \`implemented: false\`). Both require live HTTP probes against asset URLs,
902
+ which is not a synchronous-healthcheck shape.
903
+
904
+ Until a dedicated probe command exists:
905
+
906
+ - For "marked dead but still alive": sample a handful of recently-DEAD
907
+ assets, open them in a browser, and watch for any that load. Common
908
+ causes: bot protection, geo-blocking, rate limits, or liveness logic
909
+ that does not handle the asset type correctly.
910
+ - For "alive but marked dead": after a known takedown event, sample
911
+ assets that *should* be dead but are still marked alive. Common causes:
912
+ cached responses, soft-404 pages, parked-domain redirects, platform
913
+ suspension pages still returning 200.
914
+
915
+ In both cases, if liveness looks miscalibrated for a class of assets,
916
+ notify ChainPatrol engineering \u2014 the checker likely needs a tuning pass for
917
+ that platform.
772
918
  `;
773
919
  }
774
920
  function getBundledSkillVersion() {
package/dist/cli.js CHANGED
@@ -13,7 +13,7 @@ import {
13
13
  getCliVersion,
14
14
  isSkillInstalled,
15
15
  readInstalledSkillVersion
16
- } from "./chunk-S65NM7FF.js";
16
+ } from "./chunk-BSK4YHFA.js";
17
17
  import "./chunk-IUZB3DQW.js";
18
18
  import {
19
19
  DateTime
@@ -1049,7 +1049,7 @@ async function main() {
1049
1049
  thresholds.minResults = cli.flags.minResults;
1050
1050
  if (cli.flags.lookbackHours !== void 0)
1051
1051
  thresholds.lookbackHours = cli.flags.lookbackHours;
1052
- const { runHealthchecksRun } = await import("./run-7THXM7GF.js");
1052
+ const { runHealthchecksRun } = await import("./run-64SBCL4R.js");
1053
1053
  await runHealthchecksRun({
1054
1054
  org,
1055
1055
  id: action,
@@ -1095,12 +1095,12 @@ async function main() {
1095
1095
  case "setup":
1096
1096
  case "install":
1097
1097
  case "i": {
1098
- const { setupSkill } = await import("./setup-skill-Z5RVCWCU.js");
1098
+ const { setupSkill } = await import("./setup-skill-NQIZBJMR.js");
1099
1099
  setupSkill({ json: jsonMode });
1100
1100
  break;
1101
1101
  }
1102
1102
  case "uninstall": {
1103
- const { uninstallSkill } = await import("./setup-skill-Z5RVCWCU.js");
1103
+ const { uninstallSkill } = await import("./setup-skill-NQIZBJMR.js");
1104
1104
  uninstallSkill({ json: jsonMode });
1105
1105
  break;
1106
1106
  }
@@ -27,7 +27,12 @@ function buildPayload(entry, org, thresholds) {
27
27
  "failThreshold",
28
28
  "warnAgeHours",
29
29
  "failAgeHours",
30
- "staleThresholdHours"
30
+ "staleThresholdHours",
31
+ "windowHours",
32
+ "baselineDays",
33
+ "warnMultiplier",
34
+ "failMultiplier",
35
+ "minSpikeCount"
31
36
  ]);
32
37
  for (const [key, value] of Object.entries(thresholds)) {
33
38
  if (allowedKeys.has(key)) {
@@ -150,6 +155,9 @@ async function runHealthchecksRun(options) {
150
155
  if (entry.result.suggestedAction) {
151
156
  lines.push(`- Suggested action: ${entry.result.suggestedAction}`);
152
157
  }
158
+ if (entry.result.appUrl) {
159
+ lines.push(`- View in app: ${entry.result.appUrl}`);
160
+ }
153
161
  return lines.join("\n");
154
162
  }),
155
163
  ...errors.map((entry) => `## ${entry.entry.id} \u2014 ERROR
@@ -194,6 +202,10 @@ async function runHealthchecksRun(options) {
194
202
  console.log(`Suggested action for ${entry.result.id}:`);
195
203
  console.log(` ${entry.result.suggestedAction}`);
196
204
  }
205
+ if (entry.result.appUrl) {
206
+ console.log("");
207
+ console.log(`View in app: ${entry.result.appUrl}`);
208
+ }
197
209
  }
198
210
  }
199
211
  });
@@ -6,7 +6,7 @@ import {
6
6
  readInstalledSkillVersion,
7
7
  setupSkill,
8
8
  uninstallSkill
9
- } from "./chunk-S65NM7FF.js";
9
+ } from "./chunk-BSK4YHFA.js";
10
10
  import "./chunk-IUZB3DQW.js";
11
11
  export {
12
12
  getBundledSkillContent,
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@chainpatrol/cli",
3
3
  "description": "The official ChainPatrol CLI — terminal interface for threat detection",
4
4
  "author": "Umar Ahmed <umar@chainpatrol.io>",
5
- "version": "0.4.0",
5
+ "version": "0.5.0",
6
6
  "license": "UNLICENSED",
7
7
  "homepage": "https://chainpatrol.com/docs/cli",
8
8
  "keywords": [