sizmo 0.4.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/INSTALL.md +127 -0
- package/LICENSE +21 -0
- package/README.md +152 -0
- package/SKILL.md +31 -0
- package/bin/sizmo.mjs +3 -0
- package/commands/booked-not-paid.mjs +242 -0
- package/commands/brief.mjs +213 -0
- package/commands/focus.mjs +163 -0
- package/commands/noshow.mjs +117 -0
- package/commands/pipeline.mjs +147 -0
- package/commands/receivables.mjs +119 -0
- package/commands/reconcile.mjs +205 -0
- package/commands/segment.mjs +133 -0
- package/commands/snapshot.mjs +256 -0
- package/commands/triage.mjs +142 -0
- package/docs/how-to/booked-not-paid.md +54 -0
- package/docs/how-to/brief.md +71 -0
- package/docs/how-to/configure-a-client-profile.md +73 -0
- package/docs/how-to/focus.md +46 -0
- package/docs/how-to/multi-client.md +86 -0
- package/docs/how-to/noshow.md +45 -0
- package/docs/how-to/pipeline.md +47 -0
- package/docs/how-to/receivables.md +45 -0
- package/docs/how-to/reconcile.md +52 -0
- package/docs/how-to/segment.md +55 -0
- package/docs/how-to/snapshot.md +52 -0
- package/docs/how-to/triage.md +44 -0
- package/lib/cache.mjs +36 -0
- package/lib/cli.mjs +311 -0
- package/lib/config.mjs +39 -0
- package/lib/context.mjs +33 -0
- package/lib/errors.mjs +11 -0
- package/lib/http.mjs +52 -0
- package/lib/output.mjs +39 -0
- package/lib/paginate.mjs +13 -0
- package/lib/pool.mjs +10 -0
- package/lib/prioritize.mjs +146 -0
- package/lib/registry.mjs +13 -0
- package/lib/schema.mjs +9 -0
- package/package.json +45 -0
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# Multi-client workflow
|
|
2
|
+
|
|
3
|
+
`sizmo` supports multiple GoHighLevel locations via named profiles. Each profile holds one PIT and one Location ID.
|
|
4
|
+
|
|
5
|
+
## Setup — one profile per client
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
echo "pit-clientA..." | sizmo config set --profile clientA --loc LOC_A --pit-stdin
|
|
9
|
+
echo "pit-clientB..." | sizmo config set --profile clientB --loc LOC_B --pit-stdin
|
|
10
|
+
echo "pit-clientC..." | sizmo config set --profile clientC --loc LOC_C --pit-stdin
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Check all profiles
|
|
14
|
+
|
|
15
|
+
```sh
|
|
16
|
+
sizmo config list
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Output (verified format from `lib/cli.mjs`):
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
* clientA loc LOC_A pit-…AAAA day 12/90
|
|
23
|
+
clientB loc LOC_B pit-…BBBB day 8/90
|
|
24
|
+
clientC loc LOC_C pit-…CCCC day 45/90
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
`*` marks the default profile (used when `--profile` is not passed).
|
|
28
|
+
|
|
29
|
+
## Switch default
|
|
30
|
+
|
|
31
|
+
```sh
|
|
32
|
+
sizmo config use clientB
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Run a command against a specific client
|
|
36
|
+
|
|
37
|
+
Pass `--profile` to any command:
|
|
38
|
+
|
|
39
|
+
```sh
|
|
40
|
+
sizmo brief --profile clientA
|
|
41
|
+
sizmo brief --profile clientB
|
|
42
|
+
sizmo receivables --profile clientC --top 10
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Morning sweep across all clients
|
|
46
|
+
|
|
47
|
+
There is no built-in "run all profiles" command. Use a shell loop:
|
|
48
|
+
|
|
49
|
+
```sh
|
|
50
|
+
for p in clientA clientB clientC; do
|
|
51
|
+
echo "=== $p ===";
|
|
52
|
+
sizmo brief --profile $p --json;
|
|
53
|
+
done
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Or with `--json` piped to a processor:
|
|
57
|
+
|
|
58
|
+
```sh
|
|
59
|
+
for p in clientA clientB clientC; do
|
|
60
|
+
sizmo snapshot --profile $p --json
|
|
61
|
+
done
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## PIT rotation per client
|
|
65
|
+
|
|
66
|
+
Each PIT expires at 90 days. `sizmo config list` shows `day N/90` for each. Rotate before day 80:
|
|
67
|
+
|
|
68
|
+
```sh
|
|
69
|
+
echo "pit-newtoken..." | sizmo config set --profile clientA --pit-stdin --created $(date +%Y-%m-%d)
|
|
70
|
+
sizmo auth check --profile clientA
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## JSON output for automation
|
|
74
|
+
|
|
75
|
+
Every command supports `--json --profile <name>`. The envelope includes `"location"` so you can route results by location ID in your processing code:
|
|
76
|
+
|
|
77
|
+
```json
|
|
78
|
+
{
|
|
79
|
+
"schemaVersion": 1,
|
|
80
|
+
"command": "snapshot",
|
|
81
|
+
"location": "LOC_A",
|
|
82
|
+
"data": { ... },
|
|
83
|
+
"degraded": false,
|
|
84
|
+
"warnings": []
|
|
85
|
+
}
|
|
86
|
+
```
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# noshow — no-show recovery
|
|
2
|
+
|
|
3
|
+
## What it answers
|
|
4
|
+
|
|
5
|
+
"Who no-showed and hasn't been re-booked?" Lists contacts whose appointment status is `no-show` within the lookback window, so you can follow up and get them rescheduled.
|
|
6
|
+
|
|
7
|
+
## Command
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
sizmo noshow
|
|
11
|
+
sizmo noshow --days 14 # last 14 days
|
|
12
|
+
sizmo noshow --top 20 # show up to 20
|
|
13
|
+
sizmo noshow --json
|
|
14
|
+
sizmo noshow --profile myclient
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Flags (verified from `meta` in `commands/noshow.mjs`):
|
|
18
|
+
|
|
19
|
+
| Flag | Type | Default | Description |
|
|
20
|
+
|------|------|---------|-------------|
|
|
21
|
+
| `--days` | int | 30 | Lookback window |
|
|
22
|
+
| `--top` | int | 15 | Max results |
|
|
23
|
+
|
|
24
|
+
## How it works
|
|
25
|
+
|
|
26
|
+
Fetches all calendars for the location, then fetches events for each calendar within the window. Events with status `no-show` are collected, deduplicated by contact, and sorted by most recent occurrence.
|
|
27
|
+
|
|
28
|
+
**Known limitation:** GHL's `/calendars/events` endpoint does not support cursor-based pagination. If a calendar returns >= 100 events the result may be silently truncated. The CLI emits `degraded: true` with a warning when this threshold is hit. Full fix (date-window splitting) is tracked as a follow-up.
|
|
29
|
+
|
|
30
|
+
## Sample output shape (example — no live creds in this context)
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
NO-SHOWS (last 30d)
|
|
34
|
+
1. Carlo Reyes 3d ago Calendar: Consultation
|
|
35
|
+
2. Ana Lim 8d ago Calendar: Strategy Call
|
|
36
|
+
3. Bong Santos 12d ago Calendar: Consultation
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
*Sample shape only.*
|
|
40
|
+
|
|
41
|
+
## Notes
|
|
42
|
+
|
|
43
|
+
- The CLI never sends a message or re-books the appointment. Use this list to identify who to contact; the action stays with you.
|
|
44
|
+
- If `degraded: true` appears in `--json` output, at least one calendar was truncated or blocked. The list is incomplete.
|
|
45
|
+
- Contacts may appear once per no-show event. If someone no-showed twice, they may appear twice.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# pipeline — pipeline health
|
|
2
|
+
|
|
3
|
+
## What it answers
|
|
4
|
+
|
|
5
|
+
"How much value is in each stage, and which deals are stuck?" Shows total opportunity value by pipeline stage plus a stuck-deal sweep listing deals that haven't moved in N days.
|
|
6
|
+
|
|
7
|
+
## Command
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
sizmo pipeline
|
|
11
|
+
sizmo pipeline --stuck-days 14 # flag deals idle for 14+ days
|
|
12
|
+
sizmo pipeline --top 20 # show top 20 stuck deals
|
|
13
|
+
sizmo pipeline --json
|
|
14
|
+
sizmo pipeline --profile myclient
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Flags (verified from `meta` in `commands/pipeline.mjs`):
|
|
18
|
+
|
|
19
|
+
| Flag | Type | Default | Description |
|
|
20
|
+
|------|------|---------|-------------|
|
|
21
|
+
| `--stuck-days` | int | 7 | Idle threshold in days |
|
|
22
|
+
| `--top` | int | 100 | Max stuck deals to show |
|
|
23
|
+
|
|
24
|
+
## How it works
|
|
25
|
+
|
|
26
|
+
Paginates all opportunities to completion before sorting. A deal is "stuck" when its last status change, stage change, or update timestamp is older than `--stuck-days`. The stuck sweep shows value, stage, and idle age per deal.
|
|
27
|
+
|
|
28
|
+
## Sample output shape (example — no live creds in this context)
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
PIPELINE VALUE BY STAGE
|
|
32
|
+
Discovery ₱85,000 (4 deals)
|
|
33
|
+
Proposal ₱140,000 (3 deals)
|
|
34
|
+
Closed Won ₱32,000 (2 deals)
|
|
35
|
+
|
|
36
|
+
STUCK DEALS (idle > 7d)
|
|
37
|
+
1. Maria Santos Proposal ₱45,000 idle 12d
|
|
38
|
+
2. Juan dela Cruz Discovery ₱22,000 idle 9d
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
*Sample shape only.*
|
|
42
|
+
|
|
43
|
+
## Notes
|
|
44
|
+
|
|
45
|
+
- GHL opportunity `monetaryValue` has no currency field — it inherits the pipeline configuration. The CLI renders the raw value; no currency conversion is performed.
|
|
46
|
+
- `--top N` caps the stuck-deal list. All opportunities are fetched before the cap is applied.
|
|
47
|
+
- A deal last touched via `lastStatusChangeAt`, `lastStageChangeAt`, `updatedAt`, `dateUpdated`, or `dateAdded` (in that priority order) — whichever is most recent.
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# receivables — A/R who owes
|
|
2
|
+
|
|
3
|
+
## What it answers
|
|
4
|
+
|
|
5
|
+
"Who owes money, how much, and how old is the invoice?" Lists all outstanding invoices (status: sent, overdue, partially paid, payment processing, viewed, due) sorted by age descending.
|
|
6
|
+
|
|
7
|
+
## Command
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
sizmo receivables
|
|
11
|
+
sizmo receivables --top 30 # show up to 30 invoices
|
|
12
|
+
sizmo receivables --json
|
|
13
|
+
sizmo receivables --profile myclient
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Flags (verified from `meta` in `commands/receivables.mjs`):
|
|
17
|
+
|
|
18
|
+
| Flag | Type | Default | Description |
|
|
19
|
+
|------|------|---------|-------------|
|
|
20
|
+
| `--top` | int | 20 | Max rows to display |
|
|
21
|
+
|
|
22
|
+
## How it works
|
|
23
|
+
|
|
24
|
+
Paginates all invoices to completion (was previously offset-capped at 2000 — that bug is fixed). Filters to unpaid statuses. Per-currency totals are calculated separately — PHP, USD, EUR, GBP are never cross-summed.
|
|
25
|
+
|
|
26
|
+
Unpaid statuses: `sent`, `overdue`, `partially_paid`, `partially paid`, `payment_processing`, `viewed`, `due`.
|
|
27
|
+
|
|
28
|
+
## Sample output shape (example — no live creds in this context)
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
A/R — outstanding invoices
|
|
32
|
+
1. Juan dela Cruz ₱25,000 21d overdue
|
|
33
|
+
2. Maria Santos ₱18,000 14d sent
|
|
34
|
+
3. Carlo Reyes ₱8,500 7d viewed
|
|
35
|
+
|
|
36
|
+
TOTAL: ₱51,500 (PHP)
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
*Sample shape only.*
|
|
40
|
+
|
|
41
|
+
## Notes
|
|
42
|
+
|
|
43
|
+
- The CLI never sends an invoice or charges a card. To follow up, use GoHighLevel's invoice tools or your approved agent workflow.
|
|
44
|
+
- Per-currency totals are always separated. If your location has both PHP and USD invoices, each currency totals independently.
|
|
45
|
+
- `--top N` caps the display list. All invoices are fetched before the cap is applied.
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# reconcile — money collected by source
|
|
2
|
+
|
|
3
|
+
## What it answers
|
|
4
|
+
|
|
5
|
+
"How much was collected, from which payment sources, and are there any flags?" Breaks down successful transactions by payment provider/source within a window. Also surfaces subscriptions and anomaly flags.
|
|
6
|
+
|
|
7
|
+
## Command
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
sizmo reconcile
|
|
11
|
+
sizmo reconcile --days 7 # last 7 days
|
|
12
|
+
sizmo reconcile --top 30 # show top 30 sources
|
|
13
|
+
sizmo reconcile --json
|
|
14
|
+
sizmo reconcile --profile myclient
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Flags (verified from `meta` in `commands/reconcile.mjs`):
|
|
18
|
+
|
|
19
|
+
| Flag | Type | Default | Description |
|
|
20
|
+
|------|------|---------|-------------|
|
|
21
|
+
| `--days` | int | 30 | Window in days |
|
|
22
|
+
| `--top` | int | 20 | Max source rows |
|
|
23
|
+
|
|
24
|
+
## How it works
|
|
25
|
+
|
|
26
|
+
Paginates both transactions and subscriptions to completion. Groups successful transactions by source (payment provider, entity source type, or charge snapshot provider — whichever is available). Per-currency totals are kept separate. Flags transactions that look anomalous (e.g. unusually large amounts, missing source attribution).
|
|
27
|
+
|
|
28
|
+
Successful statuses: `succeeded`, `success`, `paid`, `completed`, `captured`.
|
|
29
|
+
|
|
30
|
+
## Sample output shape (example — no live creds in this context)
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
COLLECTED — last 30d
|
|
34
|
+
|
|
35
|
+
SOURCE PHP COUNT
|
|
36
|
+
stripe ₱120,000 8
|
|
37
|
+
manual ₱45,000 3
|
|
38
|
+
unknown ₱8,000 1
|
|
39
|
+
|
|
40
|
+
TOTAL: ₱173,000 (PHP)
|
|
41
|
+
|
|
42
|
+
RECURRING SUBSCRIPTIONS: 4 active
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
*Sample shape only.*
|
|
46
|
+
|
|
47
|
+
## Notes
|
|
48
|
+
|
|
49
|
+
- The CLI never refunds, voids, or charges. Read-only.
|
|
50
|
+
- Per-currency totals are always separate — never cross-summed.
|
|
51
|
+
- Source attribution relies on GHL's `paymentProviderType`, `providerType`, `source`, or `entitySourceType` fields. Transactions without any of these show as `unknown`.
|
|
52
|
+
- `--days` window is applied to transaction date. Transactions outside the window are excluded.
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# segment — find contacts by criteria
|
|
2
|
+
|
|
3
|
+
## What it answers
|
|
4
|
+
|
|
5
|
+
"Which contacts match this combination of criteria?" Filters your contact list by tag, phone presence, creation date, or tag absence. Returns a sample of matching contacts and a total count.
|
|
6
|
+
|
|
7
|
+
## Command
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
sizmo segment --tag "vip"
|
|
11
|
+
sizmo segment --no-phone
|
|
12
|
+
sizmo segment --tag "lead" --created-days 7
|
|
13
|
+
sizmo segment --without-tag "contacted" --has-phone
|
|
14
|
+
sizmo segment --no-tags
|
|
15
|
+
sizmo segment --top 50 --json
|
|
16
|
+
sizmo segment --profile myclient
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Flags (verified from `meta` in `commands/segment.mjs`):
|
|
20
|
+
|
|
21
|
+
| Flag | Type | Default | Description |
|
|
22
|
+
|------|------|---------|-------------|
|
|
23
|
+
| `--tag` | str | — | Must have this tag (case-insensitive) |
|
|
24
|
+
| `--without-tag` | str | — | Must NOT have this tag (case-insensitive) |
|
|
25
|
+
| `--no-tags` | bool | false | Contacts with zero tags |
|
|
26
|
+
| `--created-days` | int | — | Created within N days |
|
|
27
|
+
| `--has-phone` | bool | false | Must have phone number |
|
|
28
|
+
| `--no-phone` | bool | false | Must NOT have phone number |
|
|
29
|
+
| `--top` | int | 20 | Max rows to show in sample |
|
|
30
|
+
|
|
31
|
+
Flags can be combined. All criteria are ANDed.
|
|
32
|
+
|
|
33
|
+
## How it works
|
|
34
|
+
|
|
35
|
+
Paginates all contacts to completion before applying filters. Tag matching is case-insensitive. The result includes a total match count plus a sample capped at `--top`.
|
|
36
|
+
|
|
37
|
+
## Sample output shape (example — no live creds in this context)
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
SEGMENT: tag=vip, no-phone
|
|
41
|
+
Total matching: 8
|
|
42
|
+
|
|
43
|
+
1. Maria Santos (no phone) tags: vip, inquiry
|
|
44
|
+
2. Juan dela Cruz (no phone) tags: vip
|
|
45
|
+
3. Carlo Reyes (no phone) tags: vip, paid
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
*Sample shape only.*
|
|
49
|
+
|
|
50
|
+
## Notes
|
|
51
|
+
|
|
52
|
+
- The CLI never writes a tag or modifies a contact. Read-only.
|
|
53
|
+
- `--no-tags` and `--tag` are mutually exclusive in practical use — a contact with zero tags cannot also have a specific tag.
|
|
54
|
+
- Pagination exhausts all contacts before filtering. For large locations (10k+ contacts) this may take a few seconds.
|
|
55
|
+
- `--top N` caps the display sample, not the total count. The total count in the output reflects all matching contacts.
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# snapshot — 6-metric card
|
|
2
|
+
|
|
3
|
+
## What it answers
|
|
4
|
+
|
|
5
|
+
"How did the last N days look?" One-screen summary: new leads, bookings, show rate, collected revenue (per currency), conversation reply rate, and total pipeline value.
|
|
6
|
+
|
|
7
|
+
## Command
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
sizmo snapshot
|
|
11
|
+
sizmo snapshot --days 30 # 30-day window instead of 7
|
|
12
|
+
sizmo snapshot --json
|
|
13
|
+
sizmo snapshot --profile myclient
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Flags (verified from `meta` in `commands/snapshot.mjs`):
|
|
17
|
+
|
|
18
|
+
| Flag | Type | Default | Description |
|
|
19
|
+
|------|------|---------|-------------|
|
|
20
|
+
| `--days` | int | 7 | Window in days |
|
|
21
|
+
|
|
22
|
+
## Metrics
|
|
23
|
+
|
|
24
|
+
All six metrics are derived from live API reads. Each can appear as blocked (scope missing) in which case `degraded: true` is set and the value shows `⚠ can't see (reason)`.
|
|
25
|
+
|
|
26
|
+
| Metric | Source |
|
|
27
|
+
|--------|--------|
|
|
28
|
+
| New leads | Contacts created within window |
|
|
29
|
+
| Bookings | Calendar appointments created within window (all calendars) |
|
|
30
|
+
| Show rate | Attended / (Attended + No-show) within window |
|
|
31
|
+
| Collected | Sum of successful payment transactions within window, per currency |
|
|
32
|
+
| Reply rate | Conversations with an outbound message / total conversations with inbound activity |
|
|
33
|
+
| Pipeline | Sum of all open opportunity values across all pipelines |
|
|
34
|
+
|
|
35
|
+
## Sample output shape (example — no live creds in this context)
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
New leads 12
|
|
39
|
+
Bookings 8
|
|
40
|
+
Show rate 75%
|
|
41
|
+
Collected ₱48,000
|
|
42
|
+
Reply rate 83%
|
|
43
|
+
Pipeline ₱320,000
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
*Sample shape only. Actual values depend on your GoHighLevel location data.*
|
|
47
|
+
|
|
48
|
+
## Notes
|
|
49
|
+
|
|
50
|
+
- Revenue is tracked per currency — never cross-summed. If you have both PHP and USD transactions, each appears on its own line.
|
|
51
|
+
- Pipeline value uses GHL opportunity `monetaryValue`. GHL does not attach a currency field to individual opportunities — they inherit pipeline config. The CLI renders the value without conversion.
|
|
52
|
+
- Show rate requires `calendars.read` scope. If blocked, the metric shows degraded.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# triage — who's waiting on a reply
|
|
2
|
+
|
|
3
|
+
## What it answers
|
|
4
|
+
|
|
5
|
+
"Who has been waiting the longest for a response?" Surfaces conversation threads where the last message was inbound (from the contact) and no outbound reply has been sent, sorted by wait time descending.
|
|
6
|
+
|
|
7
|
+
## Command
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
sizmo triage
|
|
11
|
+
sizmo triage --top 20 # show top 20 threads
|
|
12
|
+
sizmo triage --days 14 # narrow lookback to 14 days
|
|
13
|
+
sizmo triage --json
|
|
14
|
+
sizmo triage --profile myclient
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Flags (verified from `meta` in `commands/triage.mjs`):
|
|
18
|
+
|
|
19
|
+
| Flag | Type | Default | Description |
|
|
20
|
+
|------|------|---------|-------------|
|
|
21
|
+
| `--top` | int | 10 | Max threads to show |
|
|
22
|
+
| `--days` | int | 30 | Lookback window |
|
|
23
|
+
|
|
24
|
+
## How it works
|
|
25
|
+
|
|
26
|
+
Paginates all conversations to completion (not capped at a single page) before applying `--top`, so the top N results are truly the longest-waiting — not just the first page. Each thread is examined for its last message direction. Threads where the last message was inbound and is older than the lookback threshold surface as waiting.
|
|
27
|
+
|
|
28
|
+
Channel labels: SMS, Email, Call, FB, IG, WhatsApp, GMB, Chat.
|
|
29
|
+
|
|
30
|
+
## Sample output shape (example — no live creds in this context)
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
1. Maria Santos SMS waiting 8d → reply or log
|
|
34
|
+
2. Juan dela Cruz Email waiting 5d → reply or log
|
|
35
|
+
3. Carlo Reyes WhatsApp waiting 3d → reply or log
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
*Sample shape only.*
|
|
39
|
+
|
|
40
|
+
## Notes
|
|
41
|
+
|
|
42
|
+
- `--top N` caps the final sorted list, not the pagination. All pages are fetched before the cap is applied.
|
|
43
|
+
- The CLI never sends a reply. To act on a thread, use your GoHighLevel inbox or your approved agent workflow.
|
|
44
|
+
- `--days` filters the lookback window for which conversations are considered. Threads older than the window are excluded.
|
package/lib/cache.mjs
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// lib/cache.mjs — honest on-disk TTL cache.
|
|
2
|
+
// Guardrails: short TTL, age always tracked, atomic write (temp+rename), 0700 dir / 0600 files.
|
|
3
|
+
// NEVER stores whether a response is healthy/degraded — callers only cache 2xx (see http.mjs).
|
|
4
|
+
import { createHash } from 'node:crypto';
|
|
5
|
+
import { mkdirSync, writeFileSync, readFileSync, renameSync } from 'node:fs';
|
|
6
|
+
import { join } from 'node:path';
|
|
7
|
+
import { tmpdir } from 'node:os';
|
|
8
|
+
|
|
9
|
+
function keyFile(dir, key) {
|
|
10
|
+
const h = createHash('sha256').update(key).digest('hex');
|
|
11
|
+
return join(dir, h + '.json');
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function makeCache({ dir, ttlMs, now = Date.now }) {
|
|
15
|
+
return {
|
|
16
|
+
set(key, value) {
|
|
17
|
+
try {
|
|
18
|
+
mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
19
|
+
const payload = JSON.stringify({ ts: now(), value });
|
|
20
|
+
// atomic: write to tmp then rename
|
|
21
|
+
const tmp = join(tmpdir(), 'ghl-cache-' + createHash('sha256').update(key + now()).digest('hex').slice(0, 16) + '.tmp');
|
|
22
|
+
writeFileSync(tmp, payload, { mode: 0o600 });
|
|
23
|
+
renameSync(tmp, keyFile(dir, key));
|
|
24
|
+
} catch { /* best-effort — cache write failures are non-fatal */ }
|
|
25
|
+
},
|
|
26
|
+
get(key) {
|
|
27
|
+
try {
|
|
28
|
+
const raw = readFileSync(keyFile(dir, key), 'utf8');
|
|
29
|
+
const { ts, value } = JSON.parse(raw);
|
|
30
|
+
const ageMs = now() - ts;
|
|
31
|
+
if (ageMs > ttlMs) return null; // expired
|
|
32
|
+
return { value, ageMs };
|
|
33
|
+
} catch { return null; } // missing or corrupt → null, no throw
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
}
|