@wonsukchoi/crondex 0.20.1 → 0.22.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/README.md +20 -30
- package/catalog.json +3052 -81
- package/jobs/ecommerce/affiliate-commission-payout-check.yaml +38 -0
- package/jobs/ecommerce/back-in-stock-notification-backlog-check.yaml +30 -0
- package/jobs/ecommerce/bundle-component-stock-mismatch-check.yaml +37 -0
- package/jobs/ecommerce/chargeback-fraud-risk-order-review.yaml +43 -0
- package/jobs/ecommerce/competitor-price-watch.yaml +60 -0
- package/jobs/ecommerce/economic-nexus-threshold-check.yaml +43 -0
- package/jobs/ecommerce/failed-payment-dunning-check.yaml +35 -0
- package/jobs/ecommerce/marketplace-listing-sync-drift-check.yaml +48 -0
- package/jobs/ecommerce/product-feed-health-check.yaml +35 -0
- package/jobs/ecommerce/purchase-order-overdue-check.yaml +32 -0
- package/jobs/ecommerce/storefront-ssl-domain-expiry-check.yaml +46 -0
- package/jobs/ecommerce/vip-tier-reclassification-check.yaml +55 -0
- package/jobs/ecommerce/wishlist-abandonment-check.yaml +37 -0
- package/jobs/fieldservice/maintenance-contract-renewal-check.yaml +36 -0
- package/jobs/fieldservice/parts-low-stock-reorder-check.yaml +29 -0
- package/jobs/fieldservice/tech-eta-no-show-check.yaml +32 -0
- package/jobs/fieldservice/tech-license-renewal-check.yaml +36 -0
- package/jobs/fieldservice/unpaid-invoice-aging-check.yaml +35 -0
- package/jobs/fieldservice/unresolved-callback-tracker.yaml +35 -0
- package/jobs/fieldservice/warranty-claim-expiry-check.yaml +35 -0
- package/jobs/finance/accounts-payable-aging-check.yaml +40 -0
- package/jobs/finance/bank-statement-reconciliation-check.yaml +40 -0
- package/jobs/finance/budget-vs-actual-variance-report.yaml +38 -0
- package/jobs/finance/contractor-1099-threshold-tracker.yaml +43 -0
- package/jobs/finance/credit-utilization-watch.yaml +40 -0
- package/jobs/finance/duplicate-payment-detection.yaml +43 -0
- package/jobs/finance/expense-report-compliance-check.yaml +46 -0
- package/jobs/finance/late-fee-interest-risk-check.yaml +43 -0
- package/jobs/finance/loan-covenant-compliance-check.yaml +37 -0
- package/jobs/finance/month-end-close-checklist-tracker.yaml +39 -0
- package/jobs/finance/multi-account-cash-position-rollup.yaml +45 -0
- package/jobs/finance/petty-cash-float-reconciliation.yaml +43 -0
- package/jobs/finance/quarterly-estimated-tax-calculator.yaml +66 -0
- package/jobs/finance/vendor-early-pay-discount-tracker.yaml +45 -0
- package/jobs/healthcare/appointment-slot-utilization-check.yaml +56 -0
- package/jobs/healthcare/claims-denial-resubmission-check.yaml +43 -0
- package/jobs/healthcare/clinical-supply-low-stock-check.yaml +30 -0
- package/jobs/healthcare/controlled-substance-inventory-reconciliation.yaml +42 -0
- package/jobs/healthcare/hipaa-access-log-audit.yaml +74 -0
- package/jobs/healthcare/patient-balance-collections-aging-check.yaml +41 -0
- package/jobs/healthcare/patient-immunization-schedule-check.yaml +41 -0
- package/jobs/healthcare/patient-portal-message-backlog-check.yaml +39 -0
- package/jobs/healthcare/patient-satisfaction-survey-tracking.yaml +59 -0
- package/jobs/healthcare/prescription-refill-request-backlog-check.yaml +38 -0
- package/jobs/healthcare/prior-authorization-expiry-check.yaml +53 -0
- package/jobs/healthcare/staff-cme-credit-tracking.yaml +41 -0
- package/jobs/healthcare/telehealth-session-issue-check.yaml +51 -0
- package/jobs/hr/aca-fte-threshold-monitor.yaml +44 -0
- package/jobs/hr/cobra-notification-deadline-tracker.yaml +47 -0
- package/jobs/hr/compliance-training-completion-check.yaml +41 -0
- package/jobs/hr/eeo1-filing-deadline-reminder.yaml +37 -0
- package/jobs/hr/handbook-acknowledgment-tracking.yaml +34 -0
- package/jobs/hr/headcount-budget-variance-check.yaml +37 -0
- package/jobs/hr/leave-of-absence-return-tracker.yaml +42 -0
- package/jobs/hr/org-chart-manager-drift-check.yaml +40 -0
- package/jobs/hr/pay-equity-band-outlier-check.yaml +58 -0
- package/jobs/hr/pto-year-end-use-it-or-lose-it-check.yaml +49 -0
- package/jobs/hr/retirement-plan-enrollment-deadline-check.yaml +40 -0
- package/jobs/hr/workers-comp-claim-status-check.yaml +42 -0
- package/jobs/legal/conflict-of-interest-check-tracker.yaml +38 -0
- package/jobs/legal/corporate-minute-book-completeness-check.yaml +31 -0
- package/jobs/legal/data-privacy-request-deadline-tracker.yaml +61 -0
- package/jobs/legal/entity-authority-document-expiry-check.yaml +37 -0
- package/jobs/legal/legal-document-retention-purge-review.yaml +39 -0
- package/jobs/legal/litigation-hold-acknowledgment-tracker.yaml +40 -0
- package/jobs/legal/non-compete-expiry-tracker.yaml +37 -0
- package/jobs/legal/outside-counsel-invoice-budget-check.yaml +37 -0
- package/jobs/legal/settlement-payment-schedule-tracker.yaml +34 -0
- package/jobs/legal/timekeeper-billable-hours-compliance-check.yaml +39 -0
- package/jobs/legal/trademark-infringement-watch.yaml +43 -0
- package/jobs/legal/vendor-coi-expiry-tracker.yaml +37 -0
- package/jobs/publishing/arc-review-copy-followup.yaml +35 -0
- package/jobs/publishing/isbn-registration-tracker.yaml +35 -0
- package/jobs/publishing/manuscript-deadline-tracker.yaml +34 -0
- package/jobs/publishing/permissions-clearance-tracker.yaml +36 -0
- package/jobs/publishing/print-run-reorder-threshold-check.yaml +32 -0
- package/jobs/publishing/rights-license-expiry-check.yaml +35 -0
- package/jobs/publishing/royalty-statement-due-check.yaml +33 -0
- package/jobs/realestate/capital-reserve-fund-threshold-check.yaml +32 -0
- package/jobs/realestate/eviction-notice-deadline-tracker.yaml +37 -0
- package/jobs/realestate/landlord-insurance-claim-status-check.yaml +35 -0
- package/jobs/realestate/lease-renewal-offer-window-check.yaml +35 -0
- package/jobs/realestate/maintenance-request-sla-check.yaml +39 -0
- package/jobs/realestate/move-in-move-out-inspection-tracker.yaml +35 -0
- package/jobs/realestate/rent-increase-notice-deadline-check.yaml +54 -0
- package/jobs/realestate/rent-roll-vacancy-loss-report.yaml +32 -0
- package/jobs/realestate/rental-license-renewal-check.yaml +35 -0
- package/jobs/realestate/security-deposit-return-deadline-check.yaml +38 -0
- package/jobs/realestate/tenant-application-review-backlog-check.yaml +35 -0
- package/jobs/realestate/tenant-renters-insurance-compliance-check.yaml +35 -0
- package/jobs/realestate/utility-billback-reconciliation-check.yaml +41 -0
- package/jobs/sales/champion-departure-watch.yaml +44 -0
- package/jobs/sales/closed-lost-reason-audit.yaml +42 -0
- package/jobs/sales/commission-payout-tracker.yaml +32 -0
- package/jobs/sales/contract-renewal-opportunity-tracker.yaml +28 -0
- package/jobs/sales/discount-approval-pending-check.yaml +31 -0
- package/jobs/sales/forecast-submission-compliance-check.yaml +42 -0
- package/jobs/sales/multi-year-renewal-escalation-tracker.yaml +31 -0
- package/jobs/sales/pipeline-hygiene-audit.yaml +26 -0
- package/jobs/sales/poc-trial-conversion-tracker.yaml +29 -0
- package/jobs/sales/referral-followup-tracker.yaml +40 -0
- package/jobs/sales/territory-coverage-gap-check.yaml +43 -0
- package/jobs/spa/appointment-no-show-tracker.yaml +46 -0
- package/jobs/spa/gift-card-expiry-check.yaml +35 -0
- package/jobs/spa/license-renewal-tracker.yaml +35 -0
- package/jobs/spa/membership-unused-session-expiry-check.yaml +35 -0
- package/jobs/spa/product-chemical-expiry-check.yaml +35 -0
- package/jobs/spa/retail-product-low-stock-check.yaml +26 -0
- package/jobs/spa/safety-inspection-due-reminder.yaml +33 -0
- package/jobs/telecom/circuit-outage-detection.yaml +38 -0
- package/jobs/telecom/circuit-provisioning-order-aging.yaml +36 -0
- package/jobs/telecom/cpe-firmware-update-tracker.yaml +45 -0
- package/jobs/telecom/link-capacity-threshold-alert.yaml +37 -0
- package/jobs/telecom/peering-transit-contract-renewal-check.yaml +36 -0
- package/jobs/telecom/service-ticket-churn-risk-review.yaml +56 -0
- package/jobs/telecom/sla-uptime-breach-tracker.yaml +36 -0
- package/lib/category-descriptions.js +4 -0
- package/lib/duplicates-allowlist.js +10 -0
- package/package.json +2 -2
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
id: contract-renewal-opportunity-tracker
|
|
2
|
+
version: 1
|
|
3
|
+
name: Contract Renewal Opportunity Tracker
|
|
4
|
+
description: >
|
|
5
|
+
Flags existing customer contracts approaching their renewal date so
|
|
6
|
+
account management can start the renewal conversation early. Use this
|
|
7
|
+
if a contract has ever auto-lapsed or renewed on unfavorable terms
|
|
8
|
+
because nobody flagged it until it was almost too late.
|
|
9
|
+
category: sales
|
|
10
|
+
tags: [sales, renewals, contracts, account-management]
|
|
11
|
+
schedule: "0 8 * * 1-5"
|
|
12
|
+
timezone: "UTC"
|
|
13
|
+
runner: shell
|
|
14
|
+
command: >
|
|
15
|
+
psql "{{database_url}}" -t -c "select customer_name, owner, contract_value, renewal_date from contracts where status = 'active' and renewal_date < now() + interval '{{warn_days}} days' order by renewal_date;"
|
|
16
|
+
variables:
|
|
17
|
+
database_url:
|
|
18
|
+
default: "postgres://user:pass@host:5432/db"
|
|
19
|
+
description: Connection string for the contracts/CRM database.
|
|
20
|
+
warn_days:
|
|
21
|
+
default: 60
|
|
22
|
+
description: Days before renewal_date to start flagging a contract for renewal outreach.
|
|
23
|
+
compatible_agents: [generic, claude, codex]
|
|
24
|
+
notes: >
|
|
25
|
+
Assumes a `contracts` table with `status`, `renewal_date`, `owner`, and
|
|
26
|
+
`contract_value` columns — adjust to your schema. Distinct from
|
|
27
|
+
`quote-expiry-reminder` (pre-sale quotes that haven't been signed yet);
|
|
28
|
+
this tracks signed, active customer contracts nearing their renewal date.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
id: discount-approval-pending-check
|
|
2
|
+
version: 1
|
|
3
|
+
name: Discount Approval Pending Check
|
|
4
|
+
description: >
|
|
5
|
+
Flags deals with a discount above your standard threshold that are
|
|
6
|
+
still waiting on manager approval, especially ones that have been
|
|
7
|
+
sitting for a while. Use this if a rep has ever quoted a customer a
|
|
8
|
+
deep discount and the deal stalled for days waiting on an approval
|
|
9
|
+
nobody knew was pending.
|
|
10
|
+
category: sales
|
|
11
|
+
tags: [sales, discounts, approvals, deal-desk]
|
|
12
|
+
schedule: "0 */4 * * 1-5"
|
|
13
|
+
timezone: "UTC"
|
|
14
|
+
runner: shell
|
|
15
|
+
command: >
|
|
16
|
+
psql "{{database_url}}" -t -c "select name, owner, discount_pct, approval_requested_at from deals where discount_pct >= {{discount_threshold_pct}} and approval_status = 'pending' and approval_requested_at < now() - interval '{{stale_hours}} hours' order by approval_requested_at;"
|
|
17
|
+
variables:
|
|
18
|
+
database_url:
|
|
19
|
+
default: "postgres://user:pass@host:5432/db"
|
|
20
|
+
description: Connection string for the CRM/deals database.
|
|
21
|
+
discount_threshold_pct:
|
|
22
|
+
default: 15
|
|
23
|
+
description: Discount percent that requires manager approval in your process.
|
|
24
|
+
stale_hours:
|
|
25
|
+
default: 24
|
|
26
|
+
description: Hours an approval request can sit pending before it's flagged.
|
|
27
|
+
compatible_agents: [generic, claude, codex]
|
|
28
|
+
notes: >
|
|
29
|
+
Assumes a `deals` table with `discount_pct`, `approval_status`, and
|
|
30
|
+
`approval_requested_at` columns from a deal-desk/CPQ approval workflow
|
|
31
|
+
— adjust field names to match your process.
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
id: forecast-submission-compliance-check
|
|
2
|
+
version: 1
|
|
3
|
+
name: Forecast Submission Compliance Check
|
|
4
|
+
description: >
|
|
5
|
+
Compares your active rep roster against submitted forecast records for
|
|
6
|
+
the current period and flags anyone who hasn't submitted yet. Use this
|
|
7
|
+
if sales leadership has ever walked into a forecast review meeting only
|
|
8
|
+
to find out half the team never actually submitted numbers.
|
|
9
|
+
category: sales
|
|
10
|
+
tags: [sales, forecasting, compliance, sales-ops]
|
|
11
|
+
schedule: "0 9 * * 5"
|
|
12
|
+
timezone: "UTC"
|
|
13
|
+
runner: shell
|
|
14
|
+
command: >
|
|
15
|
+
period="{{current_period}}";
|
|
16
|
+
if [ -z "$period" ]; then period=$(date +%G-W%V); fi;
|
|
17
|
+
awk -F',' -v period="$period" '
|
|
18
|
+
NR==FNR { if (FNR>1) reps[$1]=1; next }
|
|
19
|
+
FNR==1 { next }
|
|
20
|
+
$2 == period { submitted[$1]=1 }
|
|
21
|
+
END {
|
|
22
|
+
for (r in reps) {
|
|
23
|
+
if (!(r in submitted)) print "MISSING FORECAST: " r " has not submitted for " period;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
' "{{reps_csv_path}}" "{{forecasts_csv_path}}"
|
|
27
|
+
variables:
|
|
28
|
+
reps_csv_path:
|
|
29
|
+
default: "./active-reps.csv"
|
|
30
|
+
description: "Path to a CSV with header row and column: rep_name — one row per active rep expected to submit a forecast."
|
|
31
|
+
forecasts_csv_path:
|
|
32
|
+
default: "./forecast-submissions.csv"
|
|
33
|
+
description: "Path to a CSV with header row and columns: rep_name,period,submitted_at — one row per submitted forecast."
|
|
34
|
+
current_period:
|
|
35
|
+
default: ""
|
|
36
|
+
description: 'The period identifier to check submissions against (e.g. ISO week or "2026-Q3"). Leave blank to default to the current ISO week (YYYY-Www) — override if your forecasts CSV uses a different period format.'
|
|
37
|
+
compatible_agents: [generic, claude, codex]
|
|
38
|
+
notes: >
|
|
39
|
+
You maintain both CSVs yourself (export active-reps.csv from your HRIS
|
|
40
|
+
or CRM user list, and forecast-submissions.csv from wherever forecasts
|
|
41
|
+
are logged) — the `current_period` value must match how periods are
|
|
42
|
+
written in forecast-submissions.csv exactly.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
id: multi-year-renewal-escalation-tracker
|
|
2
|
+
version: 1
|
|
3
|
+
name: Multi-Year Renewal Escalation Tracker
|
|
4
|
+
description: >
|
|
5
|
+
Flags multi-year contracts approaching their next contract-year
|
|
6
|
+
anniversary, where a pre-agreed price escalation kicks in, so the
|
|
7
|
+
customer gets notice before the higher invoice arrives. Use this if a
|
|
8
|
+
customer has ever been surprised by a built-in price increase on a
|
|
9
|
+
multi-year deal because nobody sent the required notice ahead of the
|
|
10
|
+
anniversary date.
|
|
11
|
+
category: sales
|
|
12
|
+
tags: [sales, renewals, multi-year, pricing, account-management]
|
|
13
|
+
schedule: "0 8 * * 1"
|
|
14
|
+
timezone: "UTC"
|
|
15
|
+
runner: shell
|
|
16
|
+
command: >
|
|
17
|
+
psql "{{database_url}}" -t -c "select customer_name, owner, escalation_pct, next_anniversary_date from multi_year_contracts where next_anniversary_date < now() + interval '{{notice_days}} days' order by next_anniversary_date;"
|
|
18
|
+
variables:
|
|
19
|
+
database_url:
|
|
20
|
+
default: "postgres://user:pass@host:5432/db"
|
|
21
|
+
description: Connection string for the contracts/CRM database.
|
|
22
|
+
notice_days:
|
|
23
|
+
default: 45
|
|
24
|
+
description: Days of advance notice required before a contract's next anniversary/escalation date.
|
|
25
|
+
compatible_agents: [generic, claude, codex]
|
|
26
|
+
notes: >
|
|
27
|
+
Assumes a `multi_year_contracts` table with `next_anniversary_date` and
|
|
28
|
+
`escalation_pct` columns tracking the pre-agreed annual price increase
|
|
29
|
+
— adjust to your schema. Distinct from `contract-renewal-opportunity-tracker`,
|
|
30
|
+
which covers the end-of-term renewal decision rather than an in-term
|
|
31
|
+
contractual price step-up.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
id: pipeline-hygiene-audit
|
|
2
|
+
version: 1
|
|
3
|
+
name: Pipeline Hygiene Audit
|
|
4
|
+
description: >
|
|
5
|
+
Finds open deals missing basic required fields, like an amount, a close
|
|
6
|
+
date, or a next step, so forecasts and reports aren't quietly built on
|
|
7
|
+
bad data. Use this if a forecast has ever been wrong because half the
|
|
8
|
+
pipeline had blank fields nobody was checking for.
|
|
9
|
+
category: sales
|
|
10
|
+
tags: [sales, pipeline, data-quality, crm-hygiene]
|
|
11
|
+
schedule: "0 8 * * 1"
|
|
12
|
+
timezone: "UTC"
|
|
13
|
+
runner: shell
|
|
14
|
+
command: >
|
|
15
|
+
psql "{{database_url}}" -t -c "select name, owner, stage, (case when amount is null then 'missing amount' end) || (case when close_date is null then ' missing close_date' end) || (case when next_step is null or next_step = '' then ' missing next_step' end) as issues from deals where stage not in ('closed_won','closed_lost') and (amount is null or close_date is null or next_step is null or next_step = '');"
|
|
16
|
+
variables:
|
|
17
|
+
database_url:
|
|
18
|
+
default: "postgres://user:pass@host:5432/db"
|
|
19
|
+
description: Connection string for the CRM/deals database.
|
|
20
|
+
compatible_agents: [generic, claude, codex]
|
|
21
|
+
notes: >
|
|
22
|
+
Assumes a `deals` table with `amount`, `close_date`, and `next_step`
|
|
23
|
+
columns — adjust the query and the required-field list to what your
|
|
24
|
+
team actually treats as mandatory. Distinct from `stale-deal-nudge`
|
|
25
|
+
(which flags deals with no recent activity) — this flags deals with
|
|
26
|
+
incomplete data regardless of how recently they were touched.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
id: poc-trial-conversion-tracker
|
|
2
|
+
version: 1
|
|
3
|
+
name: Sales-Led POC Conversion Tracker
|
|
4
|
+
description: >
|
|
5
|
+
Flags sales-assisted proof-of-concept (POC) engagements approaching or
|
|
6
|
+
past their end date with no closed-won or closed-lost outcome recorded.
|
|
7
|
+
Use this if an enterprise POC has ever quietly run past its agreed end
|
|
8
|
+
date with no clear owner pushing toward a decision.
|
|
9
|
+
category: sales
|
|
10
|
+
tags: [sales, poc, enterprise, conversion]
|
|
11
|
+
schedule: "0 8 * * 1-5"
|
|
12
|
+
timezone: "UTC"
|
|
13
|
+
runner: shell
|
|
14
|
+
command: >
|
|
15
|
+
psql "{{database_url}}" -t -c "select account_name, owner, poc_end_date, stage from pocs where stage = 'in_progress' and poc_end_date < now() + interval '{{lookahead_days}} days' order by poc_end_date;"
|
|
16
|
+
variables:
|
|
17
|
+
database_url:
|
|
18
|
+
default: "postgres://user:pass@host:5432/db"
|
|
19
|
+
description: Connection string for the CRM/deals database (a `pocs` table or equivalent stage on `deals`).
|
|
20
|
+
lookahead_days:
|
|
21
|
+
default: 7
|
|
22
|
+
description: How many days ahead (or past, if negative logic is added) to flag a POC nearing its end date.
|
|
23
|
+
compatible_agents: [generic, claude, codex]
|
|
24
|
+
notes: >
|
|
25
|
+
Assumes a `pocs` table (or a `deals` table filtered to a POC stage) with
|
|
26
|
+
`poc_end_date` and `stage` columns — adjust to your CRM's schema. This
|
|
27
|
+
is a sales-assisted enterprise POC tracked in the CRM by a rep, distinct
|
|
28
|
+
from growth's `trial-expiring-nudge`, which tracks self-serve product
|
|
29
|
+
trials nearing expiration.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
id: referral-followup-tracker
|
|
2
|
+
version: 1
|
|
3
|
+
name: Referral Follow-Up Tracker
|
|
4
|
+
description: >
|
|
5
|
+
Flags customer referrals that were asked for or given, and haven't had a
|
|
6
|
+
logged follow-up after a set number of days. Use this if a happy
|
|
7
|
+
customer has ever offered a referral introduction and it fell through
|
|
8
|
+
the cracks because nobody owned the follow-up.
|
|
9
|
+
category: sales
|
|
10
|
+
tags: [sales, referrals, advocacy, follow-up]
|
|
11
|
+
schedule: "0 9 * * 1-5"
|
|
12
|
+
timezone: "UTC"
|
|
13
|
+
runner: shell
|
|
14
|
+
command: >
|
|
15
|
+
awk -F',' -v wait_days="{{wait_days}}" '
|
|
16
|
+
NR==1 {next}
|
|
17
|
+
$5 == "yes" {next}
|
|
18
|
+
{
|
|
19
|
+
customer=$1; referred_contact=$2; requested_date=$3; owner=$4;
|
|
20
|
+
cmd = "date -d \"" requested_date "\" +%s 2>/dev/null || date -j -f %Y-%m-%d \"" requested_date "\" +%s 2>/dev/null";
|
|
21
|
+
cmd | getline req_epoch; close(cmd);
|
|
22
|
+
"date +%s" | getline now_epoch; close("date +%s");
|
|
23
|
+
days = (now_epoch - req_epoch) / 86400;
|
|
24
|
+
if (days >= wait_days) {
|
|
25
|
+
printf "NEEDS FOLLOW-UP: %s referred %s (owner: %s) — %d day(s) since requested\n", customer, referred_contact, owner, days;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
' "{{referrals_csv_path}}"
|
|
29
|
+
variables:
|
|
30
|
+
referrals_csv_path:
|
|
31
|
+
default: "./referrals.csv"
|
|
32
|
+
description: "Path to a CSV with columns: customer_name,referred_contact,requested_date,owner,followed_up (yes/no; requested_date is YYYY-MM-DD)."
|
|
33
|
+
wait_days:
|
|
34
|
+
default: 5
|
|
35
|
+
description: Days after a referral is logged before it's flagged as overdue for a follow-up.
|
|
36
|
+
compatible_agents: [generic, claude, codex]
|
|
37
|
+
notes: >
|
|
38
|
+
You maintain referrals.csv yourself (log it when a customer offers or
|
|
39
|
+
makes an introduction, mark followed_up=yes once the owner reaches out)
|
|
40
|
+
— doesn't integrate with any CRM directly.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
id: territory-coverage-gap-check
|
|
2
|
+
version: 1
|
|
3
|
+
name: Territory Coverage Gap Check
|
|
4
|
+
description: >
|
|
5
|
+
Flags target accounts that have no assigned rep, or have an assigned
|
|
6
|
+
rep but no logged activity in a long time. Use this if an account has
|
|
7
|
+
ever gone completely unworked for months simply because it fell between
|
|
8
|
+
territory reassignments and nobody noticed the gap.
|
|
9
|
+
category: sales
|
|
10
|
+
tags: [sales, territory, account-coverage, ops]
|
|
11
|
+
schedule: "0 8 * * 1"
|
|
12
|
+
timezone: "UTC"
|
|
13
|
+
runner: shell
|
|
14
|
+
command: >
|
|
15
|
+
awk -F',' -v stale_days="{{stale_days}}" '
|
|
16
|
+
NR==1 {next}
|
|
17
|
+
{
|
|
18
|
+
account=$1; rep=$2; last_activity=$3;
|
|
19
|
+
if (rep == "" ) {
|
|
20
|
+
printf "UNASSIGNED: %s has no assigned rep\n", account;
|
|
21
|
+
next;
|
|
22
|
+
}
|
|
23
|
+
cmd = "date -d \"" last_activity "\" +%s 2>/dev/null || date -j -f %Y-%m-%d \"" last_activity "\" +%s 2>/dev/null";
|
|
24
|
+
cmd | getline last_epoch; close(cmd);
|
|
25
|
+
"date +%s" | getline now_epoch; close("date +%s");
|
|
26
|
+
days = (now_epoch - last_epoch) / 86400;
|
|
27
|
+
if (days >= stale_days) {
|
|
28
|
+
printf "NO ACTIVITY: %s (rep: %s) — %d day(s) since last activity\n", account, rep, days;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
' "{{accounts_csv_path}}"
|
|
32
|
+
variables:
|
|
33
|
+
accounts_csv_path:
|
|
34
|
+
default: "./target-accounts.csv"
|
|
35
|
+
description: "Path to a CSV with columns: account_name,assigned_rep,last_activity_date (YYYY-MM-DD; assigned_rep blank if unowned)."
|
|
36
|
+
stale_days:
|
|
37
|
+
default: 45
|
|
38
|
+
description: Days without logged activity on an assigned account before it's flagged as a coverage gap.
|
|
39
|
+
compatible_agents: [generic, claude, codex]
|
|
40
|
+
notes: >
|
|
41
|
+
You maintain target-accounts.csv yourself (or export it from your CRM's
|
|
42
|
+
account list) — this is a coverage/ownership check, not a deal-level
|
|
43
|
+
staleness check (see `stale-deal-nudge` for that).
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
id: appointment-no-show-tracker
|
|
2
|
+
version: 1
|
|
3
|
+
name: Appointment No-Show & Late-Cancel Tracker
|
|
4
|
+
description: >
|
|
5
|
+
Flags clients who no-showed or cancelled late in the past window so front
|
|
6
|
+
desk can follow up or apply a policy. Use this if a chair or treatment
|
|
7
|
+
room has ever sat empty because a client skipped without warning and
|
|
8
|
+
nobody noticed the pattern until it kept happening.
|
|
9
|
+
category: spa
|
|
10
|
+
tags: [spa, appointments, no-show, front-desk]
|
|
11
|
+
schedule: "0 18 * * *"
|
|
12
|
+
timezone: "UTC"
|
|
13
|
+
runner: shell
|
|
14
|
+
command: >
|
|
15
|
+
awk -F',' -v days="{{lookback_days}}" -v threshold="{{repeat_threshold}}" '
|
|
16
|
+
NR==1 {next}
|
|
17
|
+
{
|
|
18
|
+
cmd = "date -d \"" $2 "\" +%s 2>/dev/null || date -j -f %Y-%m-%d \"" $2 "\" +%s 2>/dev/null";
|
|
19
|
+
cmd | getline appt_epoch; close(cmd);
|
|
20
|
+
"date +%s" | getline now_epoch; close("date +%s");
|
|
21
|
+
diff_days = (now_epoch - appt_epoch) / 86400;
|
|
22
|
+
if (diff_days >= 0 && diff_days <= days && ($3 == "no-show" || $3 == "late-cancel")) {
|
|
23
|
+
count[$1]++;
|
|
24
|
+
printf "FLAGGED: %s — %s on %s\n", $1, $3, $2;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
END {
|
|
28
|
+
for (client in count) {
|
|
29
|
+
if (count[client] >= threshold) {
|
|
30
|
+
printf "REPEAT OFFENDER: %s — %d no-show/late-cancel(s) in last %d days\n", client, count[client], days;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
' "{{appointments_csv_path}}"
|
|
35
|
+
variables:
|
|
36
|
+
appointments_csv_path:
|
|
37
|
+
default: "./appointments.csv"
|
|
38
|
+
description: "Path to a CSV with columns: client_name,appointment_date (YYYY-MM-DD),status (no-show,late-cancel,completed,etc)."
|
|
39
|
+
lookback_days:
|
|
40
|
+
default: 30
|
|
41
|
+
description: How many days back to scan for no-shows and late cancels.
|
|
42
|
+
repeat_threshold:
|
|
43
|
+
default: 2
|
|
44
|
+
description: Number of no-show/late-cancel incidents in the lookback window before flagging a client as a repeat offender.
|
|
45
|
+
compatible_agents: [generic, claude, codex]
|
|
46
|
+
notes: Repeat offenders are good candidates for a deposit-required or confirm-by-text policy before their next booking.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
id: gift-card-expiry-check
|
|
2
|
+
version: 1
|
|
3
|
+
name: Gift Card Expiry Check
|
|
4
|
+
description: >
|
|
5
|
+
Scans your gift card ledger for cards approaching their expiration date
|
|
6
|
+
with a remaining balance. Use this if a client has ever held an unused
|
|
7
|
+
gift card that quietly expired, turning a goodwill purchase into a
|
|
8
|
+
complaint at checkout.
|
|
9
|
+
category: spa
|
|
10
|
+
tags: [spa, gift-cards, retention, front-desk]
|
|
11
|
+
schedule: "0 9 * * 1"
|
|
12
|
+
timezone: "UTC"
|
|
13
|
+
runner: shell
|
|
14
|
+
command: >
|
|
15
|
+
awk -F',' -v days="{{warn_days_before}}" '
|
|
16
|
+
NR==1 {next}
|
|
17
|
+
$3 > 0 {
|
|
18
|
+
cmd = "date -d \"" $2 "\" +%s 2>/dev/null || date -j -f %Y-%m-%d \"" $2 "\" +%s 2>/dev/null";
|
|
19
|
+
cmd | getline exp_epoch; close(cmd);
|
|
20
|
+
"date +%s" | getline now_epoch; close("date +%s");
|
|
21
|
+
diff_days = (exp_epoch - now_epoch) / 86400;
|
|
22
|
+
if (diff_days <= days && diff_days >= 0) {
|
|
23
|
+
printf "GIFT CARD EXPIRING: %s — balance $%.2f, expires %s (%d days)\n", $1, $3, $2, diff_days;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
' "{{gift_cards_csv_path}}"
|
|
27
|
+
variables:
|
|
28
|
+
gift_cards_csv_path:
|
|
29
|
+
default: "./gift-cards.csv"
|
|
30
|
+
description: "Path to a CSV with columns: card_code_or_holder,expiry_date (YYYY-MM-DD),remaining_balance."
|
|
31
|
+
warn_days_before:
|
|
32
|
+
default: 30
|
|
33
|
+
description: How many days before expiry to start warning, so the holder can be reminded to redeem it.
|
|
34
|
+
compatible_agents: [generic, claude, codex]
|
|
35
|
+
notes: Check local law before letting balances actually expire — many states restrict or ban gift card expiration, so this may need to become a "reminder only" job.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
id: license-renewal-tracker
|
|
2
|
+
version: 1
|
|
3
|
+
name: Esthetician/Cosmetologist License Renewal Tracker
|
|
4
|
+
description: >
|
|
5
|
+
Warns before a staff member's state license, certification, or
|
|
6
|
+
continuing-education deadline lapses. Use this if a technician has ever
|
|
7
|
+
shown up ready to work a shift, only to discover their license expired
|
|
8
|
+
last month and they can't legally touch a client.
|
|
9
|
+
category: spa
|
|
10
|
+
tags: [spa, licensing, compliance, staff]
|
|
11
|
+
schedule: "0 8 1 * *"
|
|
12
|
+
timezone: "UTC"
|
|
13
|
+
runner: shell
|
|
14
|
+
command: >
|
|
15
|
+
awk -F',' -v days="{{warn_days_before}}" '
|
|
16
|
+
NR==1 {next}
|
|
17
|
+
{
|
|
18
|
+
cmd = "date -d \"" $3 "\" +%s 2>/dev/null || date -j -f %Y-%m-%d \"" $3 "\" +%s 2>/dev/null";
|
|
19
|
+
cmd | getline exp_epoch; close(cmd);
|
|
20
|
+
"date +%s" | getline now_epoch; close("date +%s");
|
|
21
|
+
diff_days = (exp_epoch - now_epoch) / 86400;
|
|
22
|
+
if (diff_days <= days) {
|
|
23
|
+
printf "LICENSE EXPIRING: %s (%s) — expires %s (%d days)\n", $1, $2, $3, diff_days;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
' "{{licenses_csv_path}}"
|
|
27
|
+
variables:
|
|
28
|
+
licenses_csv_path:
|
|
29
|
+
default: "./staff-licenses.csv"
|
|
30
|
+
description: "Path to a CSV with columns: staff_name,license_type,expiry_date (YYYY-MM-DD)."
|
|
31
|
+
warn_days_before:
|
|
32
|
+
default: 30
|
|
33
|
+
description: How many days before expiry to start warning, leaving time to complete renewal or CE hours.
|
|
34
|
+
compatible_agents: [generic, claude, codex]
|
|
35
|
+
notes: A lapsed license can be a licensing-board violation for both the technician and the business, so warn early enough to complete any required CE hours.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
id: membership-unused-session-expiry-check
|
|
2
|
+
version: 1
|
|
3
|
+
name: Membership/Package Unused-Session Expiry Check
|
|
4
|
+
description: >
|
|
5
|
+
Flags members or package holders whose prepaid sessions or membership
|
|
6
|
+
credits are about to expire unused. Use this if a client has ever paid
|
|
7
|
+
for a monthly facial membership, banked three unused visits, and let
|
|
8
|
+
them evaporate because nobody reminded them to book.
|
|
9
|
+
category: spa
|
|
10
|
+
tags: [spa, membership, churn, retention]
|
|
11
|
+
schedule: "0 9 * * 1"
|
|
12
|
+
timezone: "UTC"
|
|
13
|
+
runner: shell
|
|
14
|
+
command: >
|
|
15
|
+
awk -F',' -v days="{{warn_days_before}}" '
|
|
16
|
+
NR==1 {next}
|
|
17
|
+
$4 > 0 {
|
|
18
|
+
cmd = "date -d \"" $3 "\" +%s 2>/dev/null || date -j -f %Y-%m-%d \"" $3 "\" +%s 2>/dev/null";
|
|
19
|
+
cmd | getline exp_epoch; close(cmd);
|
|
20
|
+
"date +%s" | getline now_epoch; close("date +%s");
|
|
21
|
+
diff_days = (exp_epoch - now_epoch) / 86400;
|
|
22
|
+
if (diff_days <= days && diff_days >= 0) {
|
|
23
|
+
printf "CREDITS EXPIRING: %s — %d unused credit(s), expires %s (%d days)\n", $1, $4, $3, diff_days;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
' "{{memberships_csv_path}}"
|
|
27
|
+
variables:
|
|
28
|
+
memberships_csv_path:
|
|
29
|
+
default: "./memberships.csv"
|
|
30
|
+
description: "Path to a CSV with columns: member_name,plan_name,credit_expiry_date (YYYY-MM-DD),unused_credits."
|
|
31
|
+
warn_days_before:
|
|
32
|
+
default: 10
|
|
33
|
+
description: How many days before credit expiry to start warning, while there's still time to book.
|
|
34
|
+
compatible_agents: [generic, claude, codex]
|
|
35
|
+
notes: A member who repeatedly lets credits expire unused is a churn risk — treat this as a retention nudge, not just a "use it or lose it" notice.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
id: product-chemical-expiry-check
|
|
2
|
+
version: 1
|
|
3
|
+
name: Product & Chemical Expiry Check
|
|
4
|
+
description: >
|
|
5
|
+
Flags skincare products, dye lots, wax, and other chemical inventory
|
|
6
|
+
approaching their expiration or period-after-opening date. Use this if
|
|
7
|
+
an esthetician or colorist has ever applied a product past its safe-use
|
|
8
|
+
date because the expiry label was never checked against today's date.
|
|
9
|
+
category: spa
|
|
10
|
+
tags: [spa, inventory, expiry, safety, chemicals]
|
|
11
|
+
schedule: "0 6 * * 1"
|
|
12
|
+
timezone: "UTC"
|
|
13
|
+
runner: shell
|
|
14
|
+
command: >
|
|
15
|
+
awk -F',' -v days="{{warn_days_before}}" '
|
|
16
|
+
NR==1 {next}
|
|
17
|
+
{
|
|
18
|
+
cmd = "date -d \"" $2 "\" +%s 2>/dev/null || date -j -f %Y-%m-%d \"" $2 "\" +%s 2>/dev/null";
|
|
19
|
+
cmd | getline exp_epoch; close(cmd);
|
|
20
|
+
"date +%s" | getline now_epoch; close("date +%s");
|
|
21
|
+
diff_days = (exp_epoch - now_epoch) / 86400;
|
|
22
|
+
if (diff_days <= days) {
|
|
23
|
+
printf "EXPIRING/EXPIRED: %s (lot %s) — expires %s (%d days)\n", $1, $3, $2, diff_days;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
' "{{product_expiry_csv_path}}"
|
|
27
|
+
variables:
|
|
28
|
+
product_expiry_csv_path:
|
|
29
|
+
default: "./product-expiry.csv"
|
|
30
|
+
description: "Path to a CSV with columns: product_name,expiry_or_pao_date (YYYY-MM-DD),lot_number."
|
|
31
|
+
warn_days_before:
|
|
32
|
+
default: 14
|
|
33
|
+
description: How many days before expiry to start warning, so stock can be used up or pulled before it goes bad.
|
|
34
|
+
compatible_agents: [generic, claude, codex]
|
|
35
|
+
notes: Applying an expired chemical (peel, dye, wax) can cause skin reactions — pull flagged items from active use immediately, don't just discount and sell through.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
id: retail-product-low-stock-check
|
|
2
|
+
version: 1
|
|
3
|
+
name: Retail Product Low-Stock Check
|
|
4
|
+
description: >
|
|
5
|
+
Scans your retail/backbar inventory log and flags skincare, haircare, or
|
|
6
|
+
nail products at or below reorder point. Use this if a stylist or
|
|
7
|
+
esthetician has ever reached for a product mid-service and found the
|
|
8
|
+
shelf empty because nobody was watching the count.
|
|
9
|
+
category: spa
|
|
10
|
+
tags: [spa, retail, inventory, restock]
|
|
11
|
+
schedule: "0 7 * * 1"
|
|
12
|
+
timezone: "UTC"
|
|
13
|
+
runner: shell
|
|
14
|
+
command: >
|
|
15
|
+
awk -F',' '
|
|
16
|
+
NR==1 {next}
|
|
17
|
+
$2 <= $3 {
|
|
18
|
+
printf "LOW STOCK: %s — %d on hand, reorder point %d\n", $1, $2, $3;
|
|
19
|
+
}
|
|
20
|
+
' "{{inventory_csv_path}}"
|
|
21
|
+
variables:
|
|
22
|
+
inventory_csv_path:
|
|
23
|
+
default: "./retail-inventory.csv"
|
|
24
|
+
description: "Path to a CSV with columns: product_name,quantity_on_hand,reorder_point."
|
|
25
|
+
compatible_agents: [generic, claude, codex]
|
|
26
|
+
notes: Pair the reorder point with your supplier's typical lead time so you never hit zero mid-service.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
id: safety-inspection-due-reminder
|
|
2
|
+
version: 1
|
|
3
|
+
name: Health & Safety Inspection Due Reminder
|
|
4
|
+
description: >
|
|
5
|
+
Warns before a health department, board-of-cosmetology, or fire/safety
|
|
6
|
+
inspection is expected, based on your jurisdiction's typical interval.
|
|
7
|
+
Use this if an inspector has ever shown up to a treatment room that
|
|
8
|
+
wasn't ready because nobody was tracking when the last inspection
|
|
9
|
+
happened.
|
|
10
|
+
category: spa
|
|
11
|
+
tags: [spa, compliance, inspection, safety]
|
|
12
|
+
schedule: "0 8 1 * *"
|
|
13
|
+
timezone: "UTC"
|
|
14
|
+
runner: shell
|
|
15
|
+
command: >
|
|
16
|
+
today=$(date +%s);
|
|
17
|
+
last_ts=$(date -d "{{last_inspection_date}}" +%s 2>/dev/null || date -jf "%Y-%m-%d" "{{last_inspection_date}}" +%s);
|
|
18
|
+
days_since=$(( (today - last_ts) / 86400 ));
|
|
19
|
+
days_left=$(( {{typical_interval_days}} - days_since ));
|
|
20
|
+
if [ "$days_left" -le "{{warn_days}}" ]; then echo "WARNING: next inspection likely due within $days_left days (last was {{last_inspection_date}})"; exit 1;
|
|
21
|
+
else echo "OK: next inspection likely due in about $days_left days"; fi
|
|
22
|
+
variables:
|
|
23
|
+
last_inspection_date:
|
|
24
|
+
default: "2026-01-01"
|
|
25
|
+
description: Date of the last health/safety/board inspection (YYYY-MM-DD).
|
|
26
|
+
typical_interval_days:
|
|
27
|
+
default: 365
|
|
28
|
+
description: Typical number of days between inspections in your jurisdiction.
|
|
29
|
+
warn_days:
|
|
30
|
+
default: 21
|
|
31
|
+
description: Days before the estimated next inspection to start warning.
|
|
32
|
+
compatible_agents: [generic, claude, codex]
|
|
33
|
+
notes: This is an estimate based on your jurisdiction's typical cadence, not a confirmed scheduled date — most boards don't publish inspection dates in advance.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
id: circuit-outage-detection
|
|
2
|
+
version: 1
|
|
3
|
+
name: Circuit Outage Detection
|
|
4
|
+
description: >
|
|
5
|
+
Pings each monitored circuit endpoint and flags any that are down.
|
|
6
|
+
Use this if a customer circuit has ever gone dark and nobody on your
|
|
7
|
+
team noticed until the customer called in.
|
|
8
|
+
category: telecom
|
|
9
|
+
tags: [telecom, outage, monitoring, circuits]
|
|
10
|
+
schedule: "*/5 * * * *"
|
|
11
|
+
timezone: "UTC"
|
|
12
|
+
runner: shell
|
|
13
|
+
command: >
|
|
14
|
+
down=0;
|
|
15
|
+
while IFS=',' read -r circuit_id endpoint _rest; do
|
|
16
|
+
[ "$circuit_id" = "circuit_id" ] && continue;
|
|
17
|
+
[ -z "$endpoint" ] && continue;
|
|
18
|
+
if ! ping -c "{{ping_count}}" -W "{{ping_timeout_sec}}" "$endpoint" >/dev/null 2>&1; then
|
|
19
|
+
echo "DOWN: circuit $circuit_id ($endpoint) is not responding";
|
|
20
|
+
down=1;
|
|
21
|
+
fi;
|
|
22
|
+
done < "{{circuits_csv_path}}";
|
|
23
|
+
if [ "$down" -eq 0 ]; then echo "OK: all monitored circuits responding"; else exit 1; fi
|
|
24
|
+
variables:
|
|
25
|
+
circuits_csv_path:
|
|
26
|
+
default: "./circuits.csv"
|
|
27
|
+
description: "Path to a CSV with columns: circuit_id,endpoint (IP or hostname to ping)."
|
|
28
|
+
ping_count:
|
|
29
|
+
default: 2
|
|
30
|
+
description: Number of ping probes per circuit before declaring it down.
|
|
31
|
+
ping_timeout_sec:
|
|
32
|
+
default: 3
|
|
33
|
+
description: Timeout in seconds per ping probe.
|
|
34
|
+
compatible_agents: [generic, claude, codex]
|
|
35
|
+
notes: >
|
|
36
|
+
Uses ICMP ping as a cheap reachability check — swap in an SNMP or NMS API
|
|
37
|
+
poll if your circuits are behind firewalls that block ICMP. Read-only,
|
|
38
|
+
no state changes.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
id: circuit-provisioning-order-aging
|
|
2
|
+
version: 1
|
|
3
|
+
name: Circuit Provisioning Order Aging Check
|
|
4
|
+
description: >
|
|
5
|
+
Flags circuit orders that were placed but never activated within a
|
|
6
|
+
target number of days. Use this if a provisioning order has ever sat
|
|
7
|
+
in limbo for weeks because the install date fell through the cracks
|
|
8
|
+
between sales, the carrier, and the field tech.
|
|
9
|
+
category: telecom
|
|
10
|
+
tags: [telecom, provisioning, circuits, orders]
|
|
11
|
+
schedule: "0 8 * * 1-5"
|
|
12
|
+
timezone: "UTC"
|
|
13
|
+
runner: shell
|
|
14
|
+
command: >
|
|
15
|
+
awk -F',' -v max_days="{{max_days_to_activate}}" '
|
|
16
|
+
NR==1 {next}
|
|
17
|
+
$3 != "" {next}
|
|
18
|
+
{
|
|
19
|
+
cmd = "date -d \"" $2 "\" +%s 2>/dev/null || date -j -f %Y-%m-%d \"" $2 "\" +%s 2>/dev/null";
|
|
20
|
+
cmd | getline order_epoch; close(cmd);
|
|
21
|
+
"date +%s" | getline now_epoch; close("date +%s");
|
|
22
|
+
age_days = (now_epoch - order_epoch) / 86400;
|
|
23
|
+
if (age_days >= max_days) {
|
|
24
|
+
printf "AGING ORDER: %s ordered %s (%d days ago), still not activated\n", $1, $2, age_days;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
' "{{orders_csv_path}}"
|
|
28
|
+
variables:
|
|
29
|
+
orders_csv_path:
|
|
30
|
+
default: "./provisioning-orders.csv"
|
|
31
|
+
description: "Path to a CSV with columns: order_id,order_date (YYYY-MM-DD),activated_date (blank if not yet activated)."
|
|
32
|
+
max_days_to_activate:
|
|
33
|
+
default: 21
|
|
34
|
+
description: Days after order date to flag as aging if still not activated.
|
|
35
|
+
compatible_agents: [generic, claude, codex]
|
|
36
|
+
notes: Assumes order/activation dates are updated in the CSV from your provisioning ticketing system.
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
id: cpe-firmware-update-tracker
|
|
2
|
+
version: 1
|
|
3
|
+
name: CPE Firmware/Warranty Update Tracker
|
|
4
|
+
description: >
|
|
5
|
+
Checks deployed customer-premises equipment against current firmware
|
|
6
|
+
versions and warranty expiry dates, flagging anything out of date or
|
|
7
|
+
soon out of warranty. Use this if a modem or router has ever been
|
|
8
|
+
running years-old firmware with a known vulnerability because nobody
|
|
9
|
+
was tracking the fleet's patch level.
|
|
10
|
+
category: telecom
|
|
11
|
+
tags: [telecom, cpe, firmware, warranty]
|
|
12
|
+
schedule: "0 8 * * 1"
|
|
13
|
+
timezone: "UTC"
|
|
14
|
+
runner: shell
|
|
15
|
+
command: >
|
|
16
|
+
awk -F',' -v target_fw="{{target_firmware_version}}" -v warn_days="{{warranty_warn_days}}" '
|
|
17
|
+
NR==1 {next}
|
|
18
|
+
{
|
|
19
|
+
device=$1; fw=$2; warranty_end=$3;
|
|
20
|
+
if (fw != target_fw) {
|
|
21
|
+
printf "OUTDATED FIRMWARE: %s running %s, target is %s\n", device, fw, target_fw;
|
|
22
|
+
}
|
|
23
|
+
if (warranty_end != "") {
|
|
24
|
+
cmd = "date -d \"" warranty_end "\" +%s 2>/dev/null || date -j -f %Y-%m-%d \"" warranty_end "\" +%s 2>/dev/null";
|
|
25
|
+
cmd | getline end_epoch; close(cmd);
|
|
26
|
+
"date +%s" | getline now_epoch; close("date +%s");
|
|
27
|
+
diff_days = (end_epoch - now_epoch) / 86400;
|
|
28
|
+
if (diff_days <= warn_days) {
|
|
29
|
+
printf "WARRANTY EXPIRING: %s warranty ends %s (%d days)\n", device, warranty_end, diff_days;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
' "{{cpe_inventory_csv_path}}"
|
|
34
|
+
variables:
|
|
35
|
+
cpe_inventory_csv_path:
|
|
36
|
+
default: "./cpe-inventory.csv"
|
|
37
|
+
description: "Path to a CSV with columns: device_id,firmware_version,warranty_end_date (YYYY-MM-DD, blank if unknown)."
|
|
38
|
+
target_firmware_version:
|
|
39
|
+
default: "1.0.0"
|
|
40
|
+
description: The firmware version the fleet should be running.
|
|
41
|
+
warranty_warn_days:
|
|
42
|
+
default: 30
|
|
43
|
+
description: Days before warranty expiry to start warning.
|
|
44
|
+
compatible_agents: [generic, claude, codex]
|
|
45
|
+
notes: Assumes CPE inventory is exported from your provisioning/asset system into the CSV before this runs.
|