@elitedcs/ghl-mcp 3.84.5 → 3.84.6
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 +6 -0
- package/README.md +1 -1
- package/dist/index.js +220 -82
- package/guide/guide.html +7 -8
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 3.84.6 — your money on the daily brief
|
|
4
|
+
|
|
5
|
+
The daily brief's Stripe section is now Money: monthly recurring revenue by product (add-ons such as extra seats roll into the product they extend), money received in the last 7 and 30 days before Stripe fees, subscription invoices paid against everything else received, and failed payment attempts. The top of the brief shows recurring revenue and the last 30 days. A Stripe key needs Read on Charges and Products for these figures; a key without them keeps the rest of the section and says which permission to add. Amounts in different currencies are never added together.
|
|
6
|
+
|
|
7
|
+
The Gmail and Google Drive panels are off the cockpit brief. Your Claude reads mail, files and calendar in its own morning brief, and the page ends by pointing there. The chat brief no longer asks Claude to save those findings.
|
|
8
|
+
|
|
3
9
|
## 3.84.5 — renewals counted again, clearer connection cards
|
|
4
10
|
|
|
5
11
|
Stripe renewals are counted again. Stripe's current API reports when a subscription renews on each of its items rather than on the subscription, so the daily brief showed no renewals due and synced records had no renewal date. Both now read it from the items.
|
package/README.md
CHANGED
package/dist/index.js
CHANGED
|
@@ -448,16 +448,16 @@ function isWriteMethod(method) {
|
|
|
448
448
|
}
|
|
449
449
|
function locationIdsInRequest(path42, opts = {}) {
|
|
450
450
|
const found = /* @__PURE__ */ new Set();
|
|
451
|
-
const
|
|
451
|
+
const add2 = (v) => {
|
|
452
452
|
if (typeof v === "string" && v.trim()) found.add(v.trim());
|
|
453
|
-
else if (Array.isArray(v)) v.forEach(
|
|
453
|
+
else if (Array.isArray(v)) v.forEach(add2);
|
|
454
454
|
};
|
|
455
455
|
const seg = /\/locations\/([^/?#]+)/i.exec(path42);
|
|
456
|
-
if (seg)
|
|
456
|
+
if (seg) add2(decodeURIComponent(seg[1]));
|
|
457
457
|
for (const src of [opts.params, opts.body]) {
|
|
458
458
|
if (!src || typeof src !== "object") continue;
|
|
459
459
|
for (const key of ["locationId", "location_id", "locationIds", "location_ids", "targetLocationId"]) {
|
|
460
|
-
|
|
460
|
+
add2(src[key]);
|
|
461
461
|
}
|
|
462
462
|
}
|
|
463
463
|
return [...found];
|
|
@@ -20079,13 +20079,13 @@ var init_registry = __esm({
|
|
|
20079
20079
|
builtState: "built",
|
|
20080
20080
|
whatItIs: "A restricted key is a password Stripe issues that lets this machine read only the parts of your account you allow.",
|
|
20081
20081
|
checkedOn: null,
|
|
20082
|
-
gives: "Adds
|
|
20083
|
-
needs: ["Access to your Stripe account's API keys.", "A restricted key with Read access to Customers, Invoices and Subscriptions."],
|
|
20082
|
+
gives: "Adds your monthly recurring revenue, money received, failed payments, who owes you and when clients renew to your brief.",
|
|
20083
|
+
needs: ["Access to your Stripe account's API keys.", "A restricted key with Read access to Customers, Invoices and Subscriptions, plus Charges and Products for money received and revenue by product."],
|
|
20084
20084
|
steps: [
|
|
20085
20085
|
"[UNVERIFIED] Sign in to your Stripe Dashboard and choose the account you want to read.",
|
|
20086
20086
|
"[UNVERIFIED] Open Developers, then API keys. If your dashboard shows Workbench, open its API keys page.",
|
|
20087
20087
|
"[UNVERIFIED] Under Restricted keys, choose Create restricted key and give it a name you recognise.",
|
|
20088
|
-
"[UNVERIFIED] Set Customers, Invoices and
|
|
20088
|
+
"[UNVERIFIED] Set Customers, Invoices, Subscriptions, Charges and Products to Read. Leave other permissions at None, then create the key.",
|
|
20089
20089
|
"[UNVERIFIED] Copy the key into the password field on the Stripe card on the Connections screen, add an account label if useful, and choose Connect. Use a live-mode key for real income."
|
|
20090
20090
|
],
|
|
20091
20091
|
say: ["Connect my Stripe.", "Sync my Stripe.", "Run my morning brief."],
|
|
@@ -20129,22 +20129,20 @@ var init_registry = __esm({
|
|
|
20129
20129
|
tools: { connect: "connect_google_calendar", disconnect: "connect_google_calendar", read: "read_my_calendar" }
|
|
20130
20130
|
},
|
|
20131
20131
|
{
|
|
20132
|
-
panel: (deps) => deps.chat("gmail", "Gmail"),
|
|
20133
20132
|
id: "gmail",
|
|
20134
20133
|
name: "Gmail",
|
|
20135
20134
|
builtState: "via-claude",
|
|
20136
20135
|
whatItIs: "Your Claude's Gmail connection lets Claude read the mail you permit in your conversation.",
|
|
20137
20136
|
checkedOn: null,
|
|
20138
20137
|
viaClaude: "Answered by your Claude's Gmail connector; nothing to connect here",
|
|
20139
|
-
gives: "
|
|
20138
|
+
gives: "Your Claude reads client email waiting on you in its own morning brief. The cockpit does not show mail.",
|
|
20140
20139
|
needs: ["Gmail connected in your Claude."],
|
|
20141
20140
|
steps: [],
|
|
20142
|
-
say: ["Run my morning brief
|
|
20141
|
+
say: ["Run my morning brief.", "What client email is waiting on me?"],
|
|
20143
20142
|
revoke: "Disconnect Gmail in your Claude's connector settings.",
|
|
20144
20143
|
tools: {}
|
|
20145
20144
|
},
|
|
20146
20145
|
{
|
|
20147
|
-
panel: (deps) => deps.chat("google-drive", "Google Drive"),
|
|
20148
20146
|
id: "google-drive",
|
|
20149
20147
|
name: "Google Drive",
|
|
20150
20148
|
builtState: "via-claude",
|
|
@@ -20154,7 +20152,7 @@ var init_registry = __esm({
|
|
|
20154
20152
|
gives: "Finds a client's agreement, onboarding form and brand files on request through your Claude's Drive connector.",
|
|
20155
20153
|
needs: ["Google Drive connected in your Claude."],
|
|
20156
20154
|
steps: [],
|
|
20157
|
-
say: ["Find this client's agreement, onboarding form and brand files in Google Drive, using their name and domain."
|
|
20155
|
+
say: ["Find this client's agreement, onboarding form and brand files in Google Drive, using their name and domain."],
|
|
20158
20156
|
revoke: "Disconnect Google Drive in your Claude's connector settings.",
|
|
20159
20157
|
tools: {}
|
|
20160
20158
|
},
|
|
@@ -20661,7 +20659,83 @@ async function health(fetcher, now = () => /* @__PURE__ */ new Date()) {
|
|
|
20661
20659
|
return { system: SYSTEM, state: "BROKEN", detail: `Stripe read failed: ${msg3.slice(0, 120)}`, checkedAt };
|
|
20662
20660
|
}
|
|
20663
20661
|
}
|
|
20664
|
-
|
|
20662
|
+
function productGroup(name, all) {
|
|
20663
|
+
const parents = all.filter((p) => p !== name && name.startsWith(p) && /^\s*[\-—–:(+]/.test(name.slice(p.length)));
|
|
20664
|
+
return parents.sort((a, b) => b.length - a.length)[0] ?? name;
|
|
20665
|
+
}
|
|
20666
|
+
function moneySummary(subs, invoices, charges, products, now = /* @__PURE__ */ new Date()) {
|
|
20667
|
+
const names = new Map(products.map((p) => [p.id, p.name]));
|
|
20668
|
+
const allNames = [...new Set(products.map((p) => p.name))];
|
|
20669
|
+
const mrr = /* @__PURE__ */ new Map();
|
|
20670
|
+
const groups = /* @__PURE__ */ new Map();
|
|
20671
|
+
let mrrUnpriced = 0;
|
|
20672
|
+
for (const sub of subs) {
|
|
20673
|
+
if (sub.status !== "active" && sub.status !== "past_due") continue;
|
|
20674
|
+
for (const item of sub.items?.data ?? []) {
|
|
20675
|
+
const price = item.price;
|
|
20676
|
+
const currency = upper(price?.currency) ?? upper(sub.currency);
|
|
20677
|
+
const per = PER_MONTH[price?.recurring?.interval ?? ""];
|
|
20678
|
+
const count2 = price?.recurring?.interval_count ?? 1;
|
|
20679
|
+
const unit = fromMinor(price?.unit_amount, currency);
|
|
20680
|
+
if (!currency || per === void 0 || unit === null || !(count2 > 0)) {
|
|
20681
|
+
mrrUnpriced++;
|
|
20682
|
+
continue;
|
|
20683
|
+
}
|
|
20684
|
+
const monthly = unit * (item.quantity ?? 1) * per / count2;
|
|
20685
|
+
add(mrr, currency, monthly);
|
|
20686
|
+
const product = price?.product ? names.get(price.product) ?? "A product no longer in Stripe" : "Unnamed product";
|
|
20687
|
+
const group = productGroup(product, allNames);
|
|
20688
|
+
const key = `${group}\0${currency}`;
|
|
20689
|
+
const g = groups.get(key) ?? { subs: /* @__PURE__ */ new Set(), currency, monthly: 0 };
|
|
20690
|
+
g.subs.add(sub.id);
|
|
20691
|
+
g.monthly += monthly;
|
|
20692
|
+
groups.set(key, g);
|
|
20693
|
+
}
|
|
20694
|
+
}
|
|
20695
|
+
const nowS = now.getTime() / 1e3;
|
|
20696
|
+
const within = (t, days) => t <= nowS && nowS - t <= days * 86400;
|
|
20697
|
+
const r7 = /* @__PURE__ */ new Map(), r30 = /* @__PURE__ */ new Map();
|
|
20698
|
+
let c7 = 0, c30 = 0, failed30 = 0;
|
|
20699
|
+
for (const c of charges) {
|
|
20700
|
+
if (!within(c.created, 30)) continue;
|
|
20701
|
+
if (c.status === "failed") {
|
|
20702
|
+
failed30++;
|
|
20703
|
+
continue;
|
|
20704
|
+
}
|
|
20705
|
+
if (c.status !== "succeeded") continue;
|
|
20706
|
+
const currency = upper(c.currency);
|
|
20707
|
+
const net = fromMinor(c.amount - (c.amount_refunded ?? 0), currency) ?? 0;
|
|
20708
|
+
if (net <= 0) continue;
|
|
20709
|
+
add(r30, currency, net);
|
|
20710
|
+
c30++;
|
|
20711
|
+
if (within(c.created, 7)) {
|
|
20712
|
+
add(r7, currency, net);
|
|
20713
|
+
c7++;
|
|
20714
|
+
}
|
|
20715
|
+
}
|
|
20716
|
+
const recurring = /* @__PURE__ */ new Map();
|
|
20717
|
+
for (const i of invoices) {
|
|
20718
|
+
const paidAt = i.status_transitions?.paid_at;
|
|
20719
|
+
const subId = i.parent?.subscription_details?.subscription ?? i.subscription;
|
|
20720
|
+
if (i.status !== "paid" || !paidAt || !within(paidAt, 30) || !subId) continue;
|
|
20721
|
+
const currency = upper(i.currency);
|
|
20722
|
+
const paid = fromMinor(i.amount_paid, currency);
|
|
20723
|
+
if (currency && paid) add(recurring, currency, paid);
|
|
20724
|
+
}
|
|
20725
|
+
const oneOff = /* @__PURE__ */ new Map();
|
|
20726
|
+
for (const [currency, amount] of r30) oneOff.set(currency, Math.max(0, amount - (recurring.get(currency) ?? 0)));
|
|
20727
|
+
return {
|
|
20728
|
+
mrr: totals(mrr),
|
|
20729
|
+
mrrUnpriced,
|
|
20730
|
+
byProduct: [...groups].map(([key, g]) => ({ name: key.split("\0")[0], subscribers: g.subs.size, currency: g.currency, monthly: Math.round(g.monthly * 100) / 100 })).sort((a, b) => b.monthly - a.monthly),
|
|
20731
|
+
received7: { count: c7, totals: totals(r7) },
|
|
20732
|
+
received30: { count: c30, totals: totals(r30) },
|
|
20733
|
+
recurring30: totals(recurring),
|
|
20734
|
+
oneOff30: totals(oneOff),
|
|
20735
|
+
failed30
|
|
20736
|
+
};
|
|
20737
|
+
}
|
|
20738
|
+
var SYSTEM, iso, ZERO_DECIMAL, THREE_DECIMAL, fromMinor, upper, stillOwed, PER_MONTH, add, totals;
|
|
20665
20739
|
var init_stripe = __esm({
|
|
20666
20740
|
"src/command-os/connectors/stripe.ts"() {
|
|
20667
20741
|
"use strict";
|
|
@@ -20681,6 +20755,9 @@ var init_stripe = __esm({
|
|
|
20681
20755
|
return t ? t : null;
|
|
20682
20756
|
};
|
|
20683
20757
|
stillOwed = (i) => (fromMinor(i.amount_remaining ?? i.amount_due ?? null, upper(i.currency)) ?? 0) > 0;
|
|
20758
|
+
PER_MONTH = { day: 365 / 12, week: 52 / 12, month: 1, year: 1 / 12 };
|
|
20759
|
+
add = (m, currency, amount) => m.set(currency, (m.get(currency) ?? 0) + amount);
|
|
20760
|
+
totals = (m) => [...m].map(([currency, amount]) => ({ currency, amount: Math.round(amount * 100) / 100 }));
|
|
20684
20761
|
}
|
|
20685
20762
|
});
|
|
20686
20763
|
|
|
@@ -21179,7 +21256,7 @@ async function connectSystem({ system, key, label }) {
|
|
|
21179
21256
|
system,
|
|
21180
21257
|
because: h.detail,
|
|
21181
21258
|
storedAt: null,
|
|
21182
|
-
note: "Nothing was saved, so nothing now claims to be connected. Fix the key and run this again \u2014 a Stripe RESTRICTED key needs read access to subscriptions, invoices and customers."
|
|
21259
|
+
note: "Nothing was saved, so nothing now claims to be connected. Fix the key and run this again \u2014 a Stripe RESTRICTED key needs read access to subscriptions, invoices and customers (and charges and products for the money figures)."
|
|
21183
21260
|
};
|
|
21184
21261
|
}
|
|
21185
21262
|
const s = connect(system, key, label, void 0, h.state === "WORKING");
|
|
@@ -21244,7 +21321,15 @@ async function readStripe(f) {
|
|
|
21244
21321
|
]);
|
|
21245
21322
|
return { subs, invoices, customers };
|
|
21246
21323
|
}
|
|
21247
|
-
|
|
21324
|
+
async function readStripeMoney(f, now) {
|
|
21325
|
+
const since = String(Math.floor(now.getTime() / 1e3) - 30 * 86400);
|
|
21326
|
+
const [charges, products] = await Promise.all([
|
|
21327
|
+
listAll(f, "/v1/charges", Charge, { "created[gte]": since }),
|
|
21328
|
+
listAll(f, "/v1/products", Product)
|
|
21329
|
+
]);
|
|
21330
|
+
return { charges, products };
|
|
21331
|
+
}
|
|
21332
|
+
var import_zod53, Customer, Subscription, Invoice, Charge, Product;
|
|
21248
21333
|
var init_stripe_read = __esm({
|
|
21249
21334
|
"src/command-os/connectors/stripe-read.ts"() {
|
|
21250
21335
|
"use strict";
|
|
@@ -21261,9 +21346,10 @@ var init_stripe_read = __esm({
|
|
|
21261
21346
|
quantity: import_zod53.z.number().nullable().optional(),
|
|
21262
21347
|
current_period_end: import_zod53.z.number().optional(),
|
|
21263
21348
|
price: import_zod53.z.object({
|
|
21349
|
+
product: import_zod53.z.string().nullable().optional(),
|
|
21264
21350
|
unit_amount: import_zod53.z.number().nullable().optional(),
|
|
21265
21351
|
currency: import_zod53.z.string().nullable().optional(),
|
|
21266
|
-
recurring: import_zod53.z.object({ interval: import_zod53.z.string().optional() }).nullable().optional()
|
|
21352
|
+
recurring: import_zod53.z.object({ interval: import_zod53.z.string().optional(), interval_count: import_zod53.z.number().optional() }).nullable().optional()
|
|
21267
21353
|
}).nullable().optional()
|
|
21268
21354
|
})).optional() }).optional()
|
|
21269
21355
|
});
|
|
@@ -21276,8 +21362,21 @@ var init_stripe_read = __esm({
|
|
|
21276
21362
|
amount_remaining: import_zod53.z.number().optional(),
|
|
21277
21363
|
due_date: import_zod53.z.number().nullable().optional(),
|
|
21278
21364
|
number: import_zod53.z.string().nullable().optional(),
|
|
21279
|
-
hosted_invoice_url: import_zod53.z.string().nullable().optional()
|
|
21365
|
+
hosted_invoice_url: import_zod53.z.string().nullable().optional(),
|
|
21366
|
+
amount_paid: import_zod53.z.number().optional(),
|
|
21367
|
+
subscription: import_zod53.z.string().nullable().optional(),
|
|
21368
|
+
parent: import_zod53.z.object({ subscription_details: import_zod53.z.object({ subscription: import_zod53.z.string().nullable().optional() }).nullable().optional() }).nullable().optional(),
|
|
21369
|
+
status_transitions: import_zod53.z.object({ paid_at: import_zod53.z.number().nullable().optional() }).nullable().optional()
|
|
21370
|
+
});
|
|
21371
|
+
Charge = import_zod53.z.object({
|
|
21372
|
+
id: import_zod53.z.string(),
|
|
21373
|
+
amount: import_zod53.z.number(),
|
|
21374
|
+
amount_refunded: import_zod53.z.number().optional(),
|
|
21375
|
+
currency: import_zod53.z.string(),
|
|
21376
|
+
status: import_zod53.z.string(),
|
|
21377
|
+
created: import_zod53.z.number()
|
|
21280
21378
|
});
|
|
21379
|
+
Product = import_zod53.z.object({ id: import_zod53.z.string(), name: import_zod53.z.string() });
|
|
21281
21380
|
}
|
|
21282
21381
|
});
|
|
21283
21382
|
|
|
@@ -22373,26 +22472,26 @@ function isHandoffAction(a, nurtureTriggerTags) {
|
|
|
22373
22472
|
function collectDefinedRefs(plan) {
|
|
22374
22473
|
const refs = /* @__PURE__ */ new Map();
|
|
22375
22474
|
const duplicates = [];
|
|
22376
|
-
const
|
|
22475
|
+
const add2 = (ref) => {
|
|
22377
22476
|
if (refs.has(ref)) duplicates.push(ref);
|
|
22378
22477
|
else refs.set(ref, refNamespace(ref));
|
|
22379
22478
|
};
|
|
22380
|
-
for (const u of plan.users ?? [])
|
|
22479
|
+
for (const u of plan.users ?? []) add2(u.ref);
|
|
22381
22480
|
for (const p of plan.pipelines ?? []) {
|
|
22382
|
-
|
|
22383
|
-
for (const s of p.stages)
|
|
22384
|
-
}
|
|
22385
|
-
for (const f of plan.customFields ?? [])
|
|
22386
|
-
for (const t of plan.tags ?? [])
|
|
22387
|
-
for (const cv of plan.customValues ?? [])
|
|
22388
|
-
for (const c of plan.calendars ?? [])
|
|
22389
|
-
for (const fm of plan.forms ?? [])
|
|
22390
|
-
for (const e of plan.emails ?? [])
|
|
22391
|
-
for (const s of plan.sms ?? [])
|
|
22392
|
-
for (const t of plan.templates?.emails ?? [])
|
|
22393
|
-
for (const t of plan.templates?.sms ?? [])
|
|
22394
|
-
for (const w of plan.workflows ?? [])
|
|
22395
|
-
for (const h of plan.handoffs ?? [])
|
|
22481
|
+
add2(p.ref);
|
|
22482
|
+
for (const s of p.stages) add2(s.ref);
|
|
22483
|
+
}
|
|
22484
|
+
for (const f of plan.customFields ?? []) add2(f.ref);
|
|
22485
|
+
for (const t of plan.tags ?? []) add2(t.ref);
|
|
22486
|
+
for (const cv of plan.customValues ?? []) add2(cv.ref);
|
|
22487
|
+
for (const c of plan.calendars ?? []) add2(c.ref);
|
|
22488
|
+
for (const fm of plan.forms ?? []) add2(fm.ref);
|
|
22489
|
+
for (const e of plan.emails ?? []) add2(e.ref);
|
|
22490
|
+
for (const s of plan.sms ?? []) add2(s.ref);
|
|
22491
|
+
for (const t of plan.templates?.emails ?? []) add2(t.ref);
|
|
22492
|
+
for (const t of plan.templates?.sms ?? []) add2(t.ref);
|
|
22493
|
+
for (const w of plan.workflows ?? []) add2(w.ref);
|
|
22494
|
+
for (const h of plan.handoffs ?? []) add2(h.ref);
|
|
22396
22495
|
return { refs, duplicates };
|
|
22397
22496
|
}
|
|
22398
22497
|
function checkRefIntegrity(plan, defined) {
|
|
@@ -24528,11 +24627,11 @@ var init_build_progress = __esm({
|
|
|
24528
24627
|
current;
|
|
24529
24628
|
updatedAt;
|
|
24530
24629
|
constructor(plan) {
|
|
24531
|
-
const
|
|
24630
|
+
const totals2 = planProgressTotals(plan);
|
|
24532
24631
|
this.tallies = PROGRESS_GROUPS.map(({ key, label }) => ({
|
|
24533
24632
|
key,
|
|
24534
24633
|
label,
|
|
24535
|
-
total:
|
|
24634
|
+
total: totals2[key],
|
|
24536
24635
|
done: 0,
|
|
24537
24636
|
deferred: 0,
|
|
24538
24637
|
failed: 0,
|
|
@@ -28412,7 +28511,7 @@ async function readDailyBrief(registry2, options = {}) {
|
|
|
28412
28511
|
function registerDailyBriefTools(server2, registry2) {
|
|
28413
28512
|
const recordTool = server2.tool(
|
|
28414
28513
|
"record_brief_answers",
|
|
28415
|
-
"Save up to 50
|
|
28514
|
+
"Save up to 50 Gmail or Google Drive findings from a chat brief as a private file on this machine, one source per call; replaces that source's previous rows and makes no provider calls. The cockpit's Daily brief page does not display them. Call it only when the user asks to save them.",
|
|
28416
28515
|
BriefAnswersInput.shape,
|
|
28417
28516
|
async (input) => {
|
|
28418
28517
|
try {
|
|
@@ -28434,7 +28533,7 @@ function registerDailyBriefTools(server2, registry2) {
|
|
|
28434
28533
|
},
|
|
28435
28534
|
async ({ live, hoursBack, quietAfterDays }) => {
|
|
28436
28535
|
const { brief } = await readDailyBrief(registry2, { live, hoursBack, quietAfterDays });
|
|
28437
|
-
return
|
|
28536
|
+
return brief;
|
|
28438
28537
|
}
|
|
28439
28538
|
);
|
|
28440
28539
|
}
|
|
@@ -28520,16 +28619,16 @@ function livePanel(id, name, read2) {
|
|
|
28520
28619
|
};
|
|
28521
28620
|
}
|
|
28522
28621
|
function money2(rows2) {
|
|
28523
|
-
const
|
|
28622
|
+
const totals2 = /* @__PURE__ */ new Map();
|
|
28524
28623
|
let unpriced = 0;
|
|
28525
28624
|
for (const row of rows2) {
|
|
28526
28625
|
if (row.amount === null || !row.currency) {
|
|
28527
28626
|
unpriced++;
|
|
28528
28627
|
continue;
|
|
28529
28628
|
}
|
|
28530
|
-
|
|
28629
|
+
totals2.set(row.currency, (totals2.get(row.currency) ?? 0) + row.amount);
|
|
28531
28630
|
}
|
|
28532
|
-
return { count: rows2.length, totals: [...
|
|
28631
|
+
return { count: rows2.length, totals: [...totals2].map(([currency, amount]) => ({ currency, amount })), unpriced };
|
|
28533
28632
|
}
|
|
28534
28633
|
function chatPanel(id, name) {
|
|
28535
28634
|
return async (_deps) => {
|
|
@@ -28553,7 +28652,7 @@ function chatPanel(id, name) {
|
|
|
28553
28652
|
async function readBriefPanels(deps) {
|
|
28554
28653
|
return Promise.all(CONNECTOR_REGISTRY.flatMap((entry) => entry.panel ? [entry.panel(briefPanelDeps(deps))] : []));
|
|
28555
28654
|
}
|
|
28556
|
-
var fs22, path20, import_node_crypto7, import_zod57, MoneySchema, BriefPanelSchema, TimesSchema, timesPath, NotConnected, safeLink, amountText, stripePanel, slackPanel, calendarPanel;
|
|
28655
|
+
var fs22, path20, import_node_crypto7, import_zod57, MoneySchema, TotalsSchema, SummarySchema, BriefPanelSchema, TimesSchema, timesPath, NotConnected, safeLink, amountText, stripePanel, slackPanel, calendarPanel;
|
|
28557
28656
|
var init_brief_panels = __esm({
|
|
28558
28657
|
"src/command-os/connectors/brief-panels.ts"() {
|
|
28559
28658
|
"use strict";
|
|
@@ -28573,6 +28672,17 @@ var init_brief_panels = __esm({
|
|
|
28573
28672
|
init_daily_brief2();
|
|
28574
28673
|
init_agency_profile();
|
|
28575
28674
|
MoneySchema = import_zod57.z.object({ count: import_zod57.z.number().int().nonnegative(), totals: import_zod57.z.array(import_zod57.z.object({ currency: import_zod57.z.string(), amount: import_zod57.z.number() })), unpriced: import_zod57.z.number().int().nonnegative() });
|
|
28675
|
+
TotalsSchema = import_zod57.z.array(import_zod57.z.object({ currency: import_zod57.z.string(), amount: import_zod57.z.number() }));
|
|
28676
|
+
SummarySchema = import_zod57.z.object({
|
|
28677
|
+
mrr: TotalsSchema,
|
|
28678
|
+
mrrUnpriced: import_zod57.z.number().int().nonnegative(),
|
|
28679
|
+
byProduct: import_zod57.z.array(import_zod57.z.object({ name: import_zod57.z.string(), subscribers: import_zod57.z.number().int().nonnegative(), currency: import_zod57.z.string(), monthly: import_zod57.z.number() })),
|
|
28680
|
+
received7: import_zod57.z.object({ count: import_zod57.z.number().int().nonnegative(), totals: TotalsSchema }),
|
|
28681
|
+
received30: import_zod57.z.object({ count: import_zod57.z.number().int().nonnegative(), totals: TotalsSchema }),
|
|
28682
|
+
recurring30: TotalsSchema,
|
|
28683
|
+
oneOff30: TotalsSchema,
|
|
28684
|
+
failed30: import_zod57.z.number().int().nonnegative()
|
|
28685
|
+
});
|
|
28576
28686
|
BriefPanelSchema = import_zod57.z.object({
|
|
28577
28687
|
id: import_zod57.z.string(),
|
|
28578
28688
|
name: import_zod57.z.string(),
|
|
@@ -28584,7 +28694,7 @@ var init_brief_panels = __esm({
|
|
|
28584
28694
|
why: import_zod57.z.string().optional(),
|
|
28585
28695
|
rows: import_zod57.z.array(BriefRowSchema),
|
|
28586
28696
|
needsYou: import_zod57.z.array(BriefRowSchema),
|
|
28587
|
-
money: import_zod57.z.object({ owed: MoneySchema, renewals: MoneySchema }).optional()
|
|
28697
|
+
money: import_zod57.z.object({ owed: MoneySchema, renewals: MoneySchema, summary: SummarySchema.optional() }).optional()
|
|
28588
28698
|
});
|
|
28589
28699
|
TimesSchema = import_zod57.z.record(import_zod57.z.string(), import_zod57.z.iso.datetime({ offset: true }));
|
|
28590
28700
|
timesPath = () => path20.join(appDataDir(), "brief-read-times.json");
|
|
@@ -28602,7 +28712,15 @@ var init_brief_panels = __esm({
|
|
|
28602
28712
|
stripePanel = livePanel("stripe", "Stripe", async ({ now, signal }) => {
|
|
28603
28713
|
if (!listConnectors({ strict: true }).some((c) => c.system === "stripe")) throw new NotConnected();
|
|
28604
28714
|
return withKey("stripe", async (key) => {
|
|
28605
|
-
const
|
|
28715
|
+
const fetcher = stripeFetcher(key, signal);
|
|
28716
|
+
const [result, paid] = await Promise.all([readStripe(fetcher), readStripeMoney(fetcher, now).then(
|
|
28717
|
+
(value) => ({ value, why: "" }),
|
|
28718
|
+
(error) => {
|
|
28719
|
+
if (error instanceof Error && error.name === "TimeoutError" || signal?.aborted) throw error;
|
|
28720
|
+
const text2 = error instanceof Error ? error.message : "";
|
|
28721
|
+
return { value: null, why: /401|403|permission/i.test(text2) ? "Money figures need Read access to Charges and Products on your Stripe key; add them in Stripe and the figures appear on the next refresh." : /more records than this read can cover/.test(text2) ? "Money figures are not shown: this account has more payments or products than one read covers." : "Money figures could not be read this time; try Refresh." };
|
|
28722
|
+
}
|
|
28723
|
+
)]);
|
|
28606
28724
|
const names = new Map(result.customers.map((c) => [c.id, c.name || c.email || c.id]));
|
|
28607
28725
|
const owed = outstanding(result.invoices, now);
|
|
28608
28726
|
const renewals = renewalsWithStale(result.subs, 30, now);
|
|
@@ -28614,8 +28732,12 @@ var init_brief_panels = __esm({
|
|
|
28614
28732
|
return {
|
|
28615
28733
|
rows: [...owed.map(invoiceRow), ...renewals.due.map((r) => ({ text: `${names.get(r.customerId) ?? r.customerId}: ${r.cancelling ? "cancels" : "renews"} at ${amountText(r.amount, r.currency)}`, when: r.renewsAt }))],
|
|
28616
28734
|
needsYou: owed.filter((i) => i.daysOverdue !== null).map(invoiceRow),
|
|
28617
|
-
money: {
|
|
28618
|
-
|
|
28735
|
+
money: {
|
|
28736
|
+
owed: money2(owed),
|
|
28737
|
+
renewals: money2(renewals.due),
|
|
28738
|
+
...paid.value ? { summary: moneySummary(result.subs, result.invoices, paid.value.charges, paid.value.products, now) } : {}
|
|
28739
|
+
},
|
|
28740
|
+
...renewals.stale.length || paid.why ? { why: [paid.why, renewals.stale.length ? `${renewals.stale.length} subscriptions have a past period end; they are excluded from renewals due.` : ""].filter(Boolean).join(" ") } : {}
|
|
28619
28741
|
};
|
|
28620
28742
|
});
|
|
28621
28743
|
});
|
|
@@ -28977,7 +29099,7 @@ var require_package = __commonJS({
|
|
|
28977
29099
|
"package.json"(exports2, module2) {
|
|
28978
29100
|
module2.exports = {
|
|
28979
29101
|
name: "@elitedcs/ghl-mcp",
|
|
28980
|
-
version: "3.84.
|
|
29102
|
+
version: "3.84.6",
|
|
28981
29103
|
mcpName: "io.github.drjerryrelth/ghl-command",
|
|
28982
29104
|
description: "GoHighLevel MCP Server for Claude. 250 tools \u2014 full CRM, automation, marketing control, account-wide workflow audit, live funnel-capture verification, and the only programmatic GHL workflow builder, now multi-tenant across client accounts.",
|
|
28983
29105
|
main: "dist/index.js",
|
|
@@ -29356,6 +29478,7 @@ main.brief{max-width:64rem;margin:auto;padding:0 1.2rem 4rem;color:var(--ink);li
|
|
|
29356
29478
|
.brief .stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(11rem,1fr));gap:.7rem;margin-top:1.4rem}
|
|
29357
29479
|
.brief .stat{background:var(--surface);border:1px solid var(--line);border-radius:var(--radius);padding:.9rem 1rem}
|
|
29358
29480
|
.brief .stat .big{display:block;font-family:var(--font-display);font-weight:600;font-size:1.5rem;line-height:1.15;color:var(--heading);font-variant-numeric:tabular-nums}
|
|
29481
|
+
.brief .stat.warn .big{color:var(--warn)}.brief .money-stats{margin:0 0 1rem}
|
|
29359
29482
|
.brief .stat .lbl{display:block;font-size:var(--fs-label);letter-spacing:.12em;text-transform:uppercase;color:var(--ink-3);margin-top:.3rem}
|
|
29360
29483
|
.brief .stats-meta{margin:.8rem 0 0;font-size:var(--fs-sm);color:var(--ink-3)}
|
|
29361
29484
|
.brief blockquote{margin:1.2rem 0 0;padding:.2rem 0 .2rem 1rem;border-left:3px solid var(--gold);font-family:var(--font-display);font-size:1.25rem;line-height:1.4;color:var(--heading)}
|
|
@@ -29450,6 +29573,8 @@ const panelDate=p=>{
|
|
|
29450
29573
|
return line;
|
|
29451
29574
|
};
|
|
29452
29575
|
const panelTone=p=>p.state==="read"?"ok":p.state==="not-connected"?"wait":p.state==="not-recorded"?"draft":"warn";
|
|
29576
|
+
const cash=(t)=>{try{return new Intl.NumberFormat(undefined,{style:"currency",currency:t.currency,maximumFractionDigits:Number.isInteger(t.amount)?0:2}).format(t.amount)}catch{return t.currency+" "+t.amount.toFixed(2)}};
|
|
29577
|
+
const cashAll=ts=>ts.length?ts.map(cash).join(" + "):cash({currency:"USD",amount:0});
|
|
29453
29578
|
const amount=m=>m.totals.length?m.totals.map(t=>t.currency+" "+t.amount.toFixed(2)).join(" + "):m.count?"Total not stated":"No amount due";
|
|
29454
29579
|
const plural=(n,one,many)=>n+" "+(n===1?one:many);
|
|
29455
29580
|
/* The work queue's titles carry the client's name ("Fix 2 things the verify
|
|
@@ -29523,8 +29648,10 @@ function draw(){
|
|
|
29523
29648
|
tile((b.readFrom?.accounts||0)+" of "+data.accounts.total,"accounts read");
|
|
29524
29649
|
tile(needs.length,"needs you today");
|
|
29525
29650
|
const stripe=data.panels.find(p=>p.id==="stripe");const money=stripe?.ok?stripe.money:null;
|
|
29526
|
-
|
|
29527
|
-
|
|
29651
|
+
const sum=money?.summary;
|
|
29652
|
+
if(sum){tile(cashAll(sum.mrr),"monthly recurring revenue");tile(cashAll(sum.received30.totals),"received before Stripe fees, last 30 days")}
|
|
29653
|
+
else if(money){tile(amount(money.owed),"owed on "+plural(money.owed.count,"open invoice","open invoices"));tile(money.renewals.count,"renewals within 30 days")}
|
|
29654
|
+
else{tile("Not read","monthly recurring revenue");tile("Not read","received before Stripe fees, last 30 days")}
|
|
29528
29655
|
tile(data.accounts.unread,"accounts could not be read");
|
|
29529
29656
|
tiles.appendChild(el("p",readLine(),"stats-meta"));
|
|
29530
29657
|
const tb=el("div","","tiles-bar");const total=data.accounts.total||0,got=b.readFrom?.accounts||0;tb.appendChild(el("span",got+" of "+total+" accounts read","lbl"));const bar=el("div","","bar");const fill=el("i");fill.style.width=(total?Math.round(100*got/total):0)+"%";bar.appendChild(fill);tb.appendChild(bar);tiles.appendChild(tb);
|
|
@@ -29565,10 +29692,20 @@ function draw(){
|
|
|
29565
29692
|
list(card(lc,"By source"),(b.leadsBySource||[]).map(r=>({text:r.client+": "+r.line})),"No source breakdown available.");
|
|
29566
29693
|
/* One panel per connected system */
|
|
29567
29694
|
data.panels.forEach(p=>{
|
|
29568
|
-
const
|
|
29695
|
+
const isMoney=p.id==="stripe";
|
|
29696
|
+
const s=section(root,"panel-"+p.id,isMoney?"From Stripe":"Connected system",isMoney?"Money":p.name,"",panelDate(p));
|
|
29569
29697
|
const head=el("div","","panel-head");head.appendChild(el("p","Results from "+p.name+" for this brief.","intro"));
|
|
29570
29698
|
const button=el("button","Refresh "+p.name,"refresh");button.type="button";button.dataset.refresh=p.id;button.onclick=()=>refreshPanel(p.id,button);head.appendChild(button);s.appendChild(head);
|
|
29571
29699
|
const c=card(s,"");
|
|
29700
|
+
const sm=p.id==="stripe"&&p.ok?p.money?.summary:null;
|
|
29701
|
+
if(sm){const g=el("div","","stats money-stats");const st=(v,l,w)=>{const b=el("div","","stat"+(w?" warn":""));b.appendChild(el("strong",String(v),"big"));b.appendChild(el("span",l,"lbl"));g.appendChild(b)};
|
|
29702
|
+
st(cashAll(sm.mrr),"monthly recurring revenue");st(cashAll(sm.received7.totals),"received before Stripe fees, last 7 days \xB7 "+plural(sm.received7.count,"payment","payments"));
|
|
29703
|
+
st(cashAll(sm.received30.totals),"received before Stripe fees, last 30 days \xB7 "+plural(sm.received30.count,"payment","payments"));st(sm.failed30,"failed payment attempts, last 30 days (Stripe retries count each time)",sm.failed30>0);c.appendChild(g);
|
|
29704
|
+
if(sm.byProduct.length){const t=el("table","","rows");const hr=el("tr");["Product","Subscribers","Monthly"].forEach(h=>hr.appendChild(el("th",h)));t.appendChild(hr);
|
|
29705
|
+
sm.byProduct.forEach(r=>{const tr=el("tr");tr.appendChild(el("td",r.name));tr.appendChild(el("td",String(r.subscribers),"num"));tr.appendChild(el("td",cash({currency:r.currency,amount:r.monthly}),"num"));t.appendChild(tr)});c.appendChild(t)}
|
|
29706
|
+
const split=el("table");const srow=(k,v)=>{const tr=el("tr");tr.appendChild(el("th",k));tr.appendChild(el("td",v,"num"));split.appendChild(tr)};
|
|
29707
|
+
srow("Subscription invoices paid, last 30 days",cashAll(sm.recurring30));srow("Everything else received, last 30 days",cashAll(sm.oneOff30));c.appendChild(split);
|
|
29708
|
+
c.appendChild(el("p","MRR is list price per month for active and past-due subscriptions. Everything else received is money in minus subscription invoices paid, never below zero."+(sm.mrrUnpriced?" "+plural(sm.mrrUnpriced,"line has no fixed price and is","lines have no fixed price and are")+" not counted.":""),"intro"))}
|
|
29572
29709
|
if(p.id==="stripe"&&p.ok&&p.money){const t=el("table");const row=(k,v)=>{const tr=el("tr");tr.appendChild(el("th",k));tr.appendChild(el("td",v,"num"));t.appendChild(tr)};
|
|
29573
29710
|
row(plural(p.money.owed.count,"open invoice","open invoices"),amount(p.money.owed));row(plural(p.money.renewals.count,"renewal within 30 days","renewals within 30 days"),amount(p.money.renewals));
|
|
29574
29711
|
if(p.money.owed.unpriced||p.money.renewals.unpriced)row("Amounts not stated",String(p.money.owed.unpriced+p.money.renewals.unpriced));c.appendChild(t)}
|
|
@@ -29576,6 +29713,7 @@ function draw(){
|
|
|
29576
29713
|
if(p.id==="stripe")rowsTable(c,marked.map(splitRow),["Customer","What","When"],p.ok?"No items returned by this source.":"");
|
|
29577
29714
|
else list(c,marked,p.ok?"No items returned by this source.":"");
|
|
29578
29715
|
});
|
|
29716
|
+
const mail=section(root,"mail","Mail, files and calendar","In your Claude brief","");mail.appendChild(el("p","Your Claude reads your mail, files and calendar in its own morning brief. Ask it: \u201CRun my morning brief.\u201D","intro"));
|
|
29579
29717
|
highlight();
|
|
29580
29718
|
}
|
|
29581
29719
|
/* The active pill follows the section in view. */
|
|
@@ -29679,15 +29817,15 @@ async function loadBriefDashboard(registry2, now = /* @__PURE__ */ new Date(), s
|
|
|
29679
29817
|
}
|
|
29680
29818
|
async function secretsKnownToThisProcess(registry2, extras = []) {
|
|
29681
29819
|
const secrets = /* @__PURE__ */ new Set();
|
|
29682
|
-
const
|
|
29820
|
+
const add2 = (key) => {
|
|
29683
29821
|
if (typeof key !== "string" || !key.trim()) throw new Error("A saved credential could not be read.");
|
|
29684
29822
|
secrets.add(key);
|
|
29685
29823
|
};
|
|
29686
29824
|
const addOptional = (key) => {
|
|
29687
29825
|
if (typeof key === "string" && !key.trim()) return;
|
|
29688
|
-
|
|
29826
|
+
add2(key);
|
|
29689
29827
|
};
|
|
29690
|
-
extras.forEach(
|
|
29828
|
+
extras.forEach(add2);
|
|
29691
29829
|
const credentialFields = [
|
|
29692
29830
|
["license_key", "GHL_LICENSE_KEY"],
|
|
29693
29831
|
["ghl_api_key", "GHL_API_KEY"],
|
|
@@ -29708,14 +29846,14 @@ async function secretsKnownToThisProcess(registry2, extras = []) {
|
|
|
29708
29846
|
}
|
|
29709
29847
|
for (const { system } of listConnectors({ strict: true })) {
|
|
29710
29848
|
await withKey(system, async (key) => {
|
|
29711
|
-
|
|
29849
|
+
add2(key);
|
|
29712
29850
|
});
|
|
29713
29851
|
}
|
|
29714
29852
|
if (calendarConnectionStatus().connected) {
|
|
29715
29853
|
await withCalendarSecret(async (secret) => {
|
|
29716
|
-
|
|
29717
|
-
|
|
29718
|
-
|
|
29854
|
+
add2(secret.clientId);
|
|
29855
|
+
add2(secret.clientSecret);
|
|
29856
|
+
add2(secret.refreshToken);
|
|
29719
29857
|
});
|
|
29720
29858
|
}
|
|
29721
29859
|
if (registry2) {
|
|
@@ -29727,7 +29865,7 @@ async function secretsKnownToThisProcess(registry2, extras = []) {
|
|
|
29727
29865
|
for (const { locationId: locationId2 } of locations) {
|
|
29728
29866
|
const token2 = registry2.getToken(locationId2);
|
|
29729
29867
|
if (!token2) throw new Error("Account credentials could not be read.");
|
|
29730
|
-
|
|
29868
|
+
add2(token2.apiKey);
|
|
29731
29869
|
}
|
|
29732
29870
|
const agency = registry2.getAgencyKey();
|
|
29733
29871
|
if (agency !== void 0) addOptional(agency);
|
|
@@ -31311,7 +31449,7 @@ function scrubForClient(text2) {
|
|
|
31311
31449
|
}
|
|
31312
31450
|
function clientNextSteps(items) {
|
|
31313
31451
|
const steps = [];
|
|
31314
|
-
const
|
|
31452
|
+
const add2 = (s) => {
|
|
31315
31453
|
if (s && !steps.includes(s)) steps.push(s);
|
|
31316
31454
|
};
|
|
31317
31455
|
for (const it of items) {
|
|
@@ -31319,12 +31457,12 @@ function clientNextSteps(items) {
|
|
|
31319
31457
|
if (it.state === "waiting") {
|
|
31320
31458
|
const who = scrubForClient(it.resolvedBy ?? "");
|
|
31321
31459
|
if (who) {
|
|
31322
|
-
|
|
31460
|
+
add2(`${it.title}: ${who}${it.roughly ? ` \u2014 roughly ${scrubForClient(it.roughly)}` : ""}`);
|
|
31323
31461
|
continue;
|
|
31324
31462
|
}
|
|
31325
31463
|
}
|
|
31326
31464
|
const rule5 = NEXT_STEP_RULES.find((r) => r.re.test(it.sentence));
|
|
31327
|
-
|
|
31465
|
+
add2(rule5 ? rule5.step : `Confirm "${it.title}" with your team once the item above has been sorted.`);
|
|
31328
31466
|
}
|
|
31329
31467
|
return steps;
|
|
31330
31468
|
}
|
|
@@ -34644,7 +34782,7 @@ function compute(ans, skip, asm) {
|
|
|
34644
34782
|
O.push(o);
|
|
34645
34783
|
return o;
|
|
34646
34784
|
}
|
|
34647
|
-
function
|
|
34785
|
+
function add2(id, title2, needs, assumption, fn, fix) {
|
|
34648
34786
|
var missing = needs.filter(function(n) {
|
|
34649
34787
|
return (n.share ? share(n.id) : raw(n.id)) === null;
|
|
34650
34788
|
}).map(function(n) {
|
|
@@ -34675,7 +34813,7 @@ function compute(ans, skip, asm) {
|
|
|
34675
34813
|
}
|
|
34676
34814
|
F.push({ id, title: title2, monthly: m, assumption: assumption || null, fix });
|
|
34677
34815
|
}
|
|
34678
|
-
|
|
34816
|
+
add2(
|
|
34679
34817
|
"missed",
|
|
34680
34818
|
"Calls you never get back to",
|
|
34681
34819
|
[{ id: "calls_missed" }, { id: "missed_never_return", share: true }],
|
|
@@ -34685,7 +34823,7 @@ function compute(ans, skip, asm) {
|
|
|
34685
34823
|
},
|
|
34686
34824
|
"Call answering, day and night"
|
|
34687
34825
|
);
|
|
34688
|
-
|
|
34826
|
+
add2(
|
|
34689
34827
|
"waited",
|
|
34690
34828
|
"Enquiries that waited",
|
|
34691
34829
|
[{ id: "waited_month" }],
|
|
@@ -34703,7 +34841,7 @@ function compute(ans, skip, asm) {
|
|
|
34703
34841
|
reason: "you told us more quotes were won than were sent \u2014 worth checking before this is priced"
|
|
34704
34842
|
});
|
|
34705
34843
|
} else {
|
|
34706
|
-
|
|
34844
|
+
add2(
|
|
34707
34845
|
"quotes",
|
|
34708
34846
|
"Quotes nobody chases",
|
|
34709
34847
|
[{ id: "quotes_month" }, { id: "quotes_won" }],
|
|
@@ -34714,7 +34852,7 @@ function compute(ans, skip, asm) {
|
|
|
34714
34852
|
"Follow-up that runs itself"
|
|
34715
34853
|
);
|
|
34716
34854
|
}
|
|
34717
|
-
|
|
34855
|
+
add2(
|
|
34718
34856
|
"noshow",
|
|
34719
34857
|
"No-shows nobody rebooks",
|
|
34720
34858
|
[{ id: "booked_month" }, { id: "show_rate", share: true }],
|
|
@@ -50553,18 +50691,18 @@ function owedByClient(owing, identities, links, nameFor2) {
|
|
|
50553
50691
|
const byCurrency = /* @__PURE__ */ new Map();
|
|
50554
50692
|
const unattributedByCurrency = /* @__PURE__ */ new Map();
|
|
50555
50693
|
let unattributedCount = 0;
|
|
50556
|
-
const
|
|
50694
|
+
const add2 = (m, currency, amount) => {
|
|
50557
50695
|
const c = currency ?? "unstated";
|
|
50558
50696
|
m.set(c, (m.get(c) ?? 0) + amount);
|
|
50559
50697
|
};
|
|
50560
50698
|
for (const o of owing) {
|
|
50561
50699
|
const tenant = tenantOfCustomer.get(o.customerId);
|
|
50562
50700
|
if (!tenant) {
|
|
50563
|
-
|
|
50701
|
+
add2(unattributedByCurrency, o.currency, o.amount);
|
|
50564
50702
|
unattributedCount += 1;
|
|
50565
50703
|
continue;
|
|
50566
50704
|
}
|
|
50567
|
-
|
|
50705
|
+
add2(byCurrency, o.currency, o.amount);
|
|
50568
50706
|
lines.push({
|
|
50569
50707
|
client: nameFor2(tenant),
|
|
50570
50708
|
amount: o.amount,
|
|
@@ -51636,7 +51774,7 @@ function isIdShapedKey(key) {
|
|
|
51636
51774
|
}
|
|
51637
51775
|
function scanArgs(args, maxNodes = MAX_ARG_NODES) {
|
|
51638
51776
|
const out = { ids: [], locations: [], unknown: [], uncheckable: [] };
|
|
51639
|
-
const
|
|
51777
|
+
const add2 = (kind, v, key) => {
|
|
51640
51778
|
const list2 = Array.isArray(v) ? v : [v];
|
|
51641
51779
|
for (const x of list2) {
|
|
51642
51780
|
const id = typeof x === "number" ? String(x) : x;
|
|
@@ -51672,28 +51810,28 @@ function scanArgs(args, maxNodes = MAX_ARG_NODES) {
|
|
|
51672
51810
|
if (k === "locationId") {
|
|
51673
51811
|
if (typeof x === "string" && x.length > 0) out.locations.push(x);
|
|
51674
51812
|
} else if (k === "traffic_weightage" && isRecord10(x)) {
|
|
51675
|
-
|
|
51813
|
+
add2("user", Object.keys(x), k);
|
|
51676
51814
|
continue;
|
|
51677
51815
|
} else if (k === "traffic_index" && Array.isArray(x)) {
|
|
51678
|
-
|
|
51816
|
+
add2("user", x.map((e) => isRecord10(e) ? e.id : void 0), k);
|
|
51679
51817
|
} else if (k === "selectedUser") {
|
|
51680
|
-
if (v.userType !== "all")
|
|
51818
|
+
if (v.userType !== "all") add2("user", x, k);
|
|
51681
51819
|
} else if (k === "field" && inFields) {
|
|
51682
51820
|
if (typeof x === "string") {
|
|
51683
51821
|
const standardKey = STANDARD_CONTACT_FIELDS.has(x.toLowerCase());
|
|
51684
|
-
if (!standardKey || isIdShaped(x))
|
|
51822
|
+
if (!standardKey || isIdShaped(x)) add2("custom_field", x, "fields[].field");
|
|
51685
51823
|
}
|
|
51686
51824
|
} else if (k === "customFields" && Array.isArray(x)) {
|
|
51687
|
-
|
|
51825
|
+
add2("custom_field", x.map((e) => isRecord10(e) ? e.id : void 0), "customFields[].id");
|
|
51688
51826
|
} else if (k === "customField" && isRecord10(x)) {
|
|
51689
|
-
|
|
51827
|
+
add2("custom_field", x.id, "customField.id");
|
|
51690
51828
|
} else if (Object.hasOwn(ID_ARG_KINDS, k)) {
|
|
51691
|
-
|
|
51829
|
+
add2(ID_ARG_KINDS[k], x, k);
|
|
51692
51830
|
} else if (isIdShapedKey(k) && !Object.hasOwn(NOT_CHECKABLE_ID_ARGS, k) && hasValue(x)) {
|
|
51693
51831
|
out.unknown.push({ key: k, value: JSON.stringify(x).slice(0, 80), ...nodeType ? { nodeType } : {} });
|
|
51694
51832
|
}
|
|
51695
51833
|
if ((k === "filterField" || k === "field" && !inFields) && typeof x === "string" && Object.hasOwn(CONDITION_FIELD_KINDS, x)) {
|
|
51696
|
-
|
|
51834
|
+
add2(CONDITION_FIELD_KINDS[x], Object.hasOwn(v, "secondValue") ? v.secondValue : v.value, `${k} ${x}`);
|
|
51697
51835
|
}
|
|
51698
51836
|
if (x && typeof x === "object") stack.push({ v: x, inFields: k === "fields" && Array.isArray(x), nodeType });
|
|
51699
51837
|
}
|
|
@@ -53770,11 +53908,11 @@ function previewShowsPlanBody(preview, planHtml) {
|
|
|
53770
53908
|
}
|
|
53771
53909
|
function bindingIdentities(plan) {
|
|
53772
53910
|
const result = [];
|
|
53773
|
-
const
|
|
53774
|
-
for (const u of planUsers(plan))
|
|
53911
|
+
const add2 = (type, ref, name, identity2 = name) => result.push({ ref, type, name, identity: `${type}:${norm5(identity2)}` });
|
|
53912
|
+
for (const u of planUsers(plan)) add2("user", u.ref, userDisplayName(u), u.email);
|
|
53775
53913
|
for (const p of plan.pipelines ?? []) {
|
|
53776
|
-
|
|
53777
|
-
for (const stage of p.stages)
|
|
53914
|
+
add2("pipeline", p.ref, p.name);
|
|
53915
|
+
for (const stage of p.stages) add2("stage", stage.ref, stage.name, `${p.ref}:${p.name}:${stage.name}`);
|
|
53778
53916
|
}
|
|
53779
53917
|
for (const [type, objects] of [
|
|
53780
53918
|
["field", plan.customFields],
|
|
@@ -53785,7 +53923,7 @@ function bindingIdentities(plan) {
|
|
|
53785
53923
|
["workflow", plan.workflows],
|
|
53786
53924
|
["email_template", planEmailTemplates(plan)],
|
|
53787
53925
|
["sms_template", planSmsTemplates(plan)]
|
|
53788
|
-
]) for (const obj of objects ?? [])
|
|
53926
|
+
]) for (const obj of objects ?? []) add2(type, obj.ref, obj.name);
|
|
53789
53927
|
return result;
|
|
53790
53928
|
}
|
|
53791
53929
|
function matchingPriorBindings(plan, previous, ids = {}) {
|
package/guide/guide.html
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
<meta charset="utf-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
6
|
<title>GHL Command — User Guide</title>
|
|
7
|
-
<!-- guides-hash:
|
|
7
|
+
<!-- guides-hash: b807bb53aa200532 -->
|
|
8
8
|
<style>
|
|
9
9
|
/* Deliberately light in every environment, including a dark-mode OS. This is a
|
|
10
10
|
reference document people read at length, print, and save to PDF, and a page
|
|
@@ -1209,12 +1209,12 @@ Before you build: look up the real calendar, the real workflows you are removing
|
|
|
1209
1209
|
<p class="gpara">A restricted key is a password Stripe issues that lets this machine read only the parts of your account you allow.</p>
|
|
1210
1210
|
<p class="gpara">not yet checked</p>
|
|
1211
1211
|
<h3>What it gives you</h3>
|
|
1212
|
-
<p class="gpara">Adds
|
|
1212
|
+
<p class="gpara">Adds your monthly recurring revenue, money received, failed payments, who owes you and when clients renew to your brief.</p>
|
|
1213
1213
|
<h3>What you need</h3>
|
|
1214
1214
|
<p class="gpara">Have these ready before you connect.</p>
|
|
1215
1215
|
<ul>
|
|
1216
1216
|
<li>Access to your Stripe account's API keys.</li>
|
|
1217
|
-
<li>A restricted key with Read access to Customers, Invoices and Subscriptions.</li>
|
|
1217
|
+
<li>A restricted key with Read access to Customers, Invoices and Subscriptions, plus Charges and Products for money received and revenue by product.</li>
|
|
1218
1218
|
</ul>
|
|
1219
1219
|
<h3>How to connect</h3>
|
|
1220
1220
|
<p class="gpara">Follow these steps to give this machine access.</p>
|
|
@@ -1223,7 +1223,7 @@ Before you build: look up the real calendar, the real workflows you are removing
|
|
|
1223
1223
|
<li>Sign in to your Stripe Dashboard and choose the account you want to read. (not yet checked on a real screen)</li>
|
|
1224
1224
|
<li>Open Developers, then API keys. If your dashboard shows Workbench, open its API keys page. (not yet checked on a real screen)</li>
|
|
1225
1225
|
<li>Under Restricted keys, choose Create restricted key and give it a name you recognise. (not yet checked on a real screen)</li>
|
|
1226
|
-
<li>Set Customers, Invoices and
|
|
1226
|
+
<li>Set Customers, Invoices, Subscriptions, Charges and Products to Read. Leave other permissions at None, then create the key. (not yet checked on a real screen)</li>
|
|
1227
1227
|
<li>Copy the key into the password field on the Stripe card on the Connections screen, add an account label if useful, and choose Connect. Use a live-mode key for real income. (not yet checked on a real screen)</li>
|
|
1228
1228
|
</ol>
|
|
1229
1229
|
<h3>What to say</h3>
|
|
@@ -1305,7 +1305,7 @@ Before you build: look up the real calendar, the real workflows you are removing
|
|
|
1305
1305
|
<div class="gsec"><h2>Gmail</h2>
|
|
1306
1306
|
<p class="gpara">Your Claude's Gmail connection lets Claude read the mail you permit in your conversation.</p>
|
|
1307
1307
|
<h3>What it gives you</h3>
|
|
1308
|
-
<p class="gpara">
|
|
1308
|
+
<p class="gpara">Your Claude reads client email waiting on you in its own morning brief. The cockpit does not show mail.</p>
|
|
1309
1309
|
<h3>What you need</h3>
|
|
1310
1310
|
<p class="gpara">Have these ready before you connect.</p>
|
|
1311
1311
|
<ul>
|
|
@@ -1316,8 +1316,8 @@ Before you build: look up the real calendar, the real workflows you are removing
|
|
|
1316
1316
|
<h3>What to say</h3>
|
|
1317
1317
|
<p class="gpara">In a Claude chat you can say one of these to connect or use it.</p>
|
|
1318
1318
|
<ul>
|
|
1319
|
-
<li>“Run my morning brief
|
|
1320
|
-
<li>“
|
|
1319
|
+
<li>“Run my morning brief.”</li>
|
|
1320
|
+
<li>“What client email is waiting on me?”</li>
|
|
1321
1321
|
</ul>
|
|
1322
1322
|
<h3>How to take access back</h3>
|
|
1323
1323
|
<p class="gpara">Remove the saved connection or take back the provider's permission.</p>
|
|
@@ -1338,7 +1338,6 @@ Before you build: look up the real calendar, the real workflows you are removing
|
|
|
1338
1338
|
<p class="gpara">In a Claude chat you can say one of these to connect or use it.</p>
|
|
1339
1339
|
<ul>
|
|
1340
1340
|
<li>“Find this client's agreement, onboarding form and brand files in Google Drive, using their name and domain.”</li>
|
|
1341
|
-
<li>“After checking Drive on request, call record_brief_answers to put the results on my brief dashboard.”</li>
|
|
1342
1341
|
</ul>
|
|
1343
1342
|
<h3>How to take access back</h3>
|
|
1344
1343
|
<p class="gpara">Remove the saved connection or take back the provider's permission.</p>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elitedcs/ghl-mcp",
|
|
3
|
-
"version": "3.84.
|
|
3
|
+
"version": "3.84.6",
|
|
4
4
|
"mcpName": "io.github.drjerryrelth/ghl-command",
|
|
5
5
|
"description": "GoHighLevel MCP Server for Claude. 250 tools — full CRM, automation, marketing control, account-wide workflow audit, live funnel-capture verification, and the only programmatic GHL workflow builder, now multi-tenant across client accounts.",
|
|
6
6
|
"main": "dist/index.js",
|