@elitedcs/ghl-mcp 3.83.4 → 3.83.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 +56 -0
- package/README.md +1 -1
- package/dist/index.js +43 -15
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,57 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 3.83.6 — what a design run actually costs you, measured
|
|
6
|
+
|
|
7
|
+
3.83.5 gave a running job a clock. This gives it an honest number on it.
|
|
8
|
+
|
|
9
|
+
How long a run takes was written by hand in three places and every one was wrong.
|
|
10
|
+
The brand page said a deck takes "roughly ten minutes". The run API said "twenty
|
|
11
|
+
minutes or more". The clock added in 3.83.5 said "ten to twenty". Nobody had ever
|
|
12
|
+
timed one.
|
|
13
|
+
|
|
14
|
+
Two runs have now been measured. A six-slide deck took 12 minutes 39 seconds and
|
|
15
|
+
$1.74 for a single pass. A conversion page on a real brand took 26 minutes and
|
|
16
|
+
$3.75 across three. So a pass is 9 to 13 minutes and $1.25 to $1.75, and a run may
|
|
17
|
+
use three of them.
|
|
18
|
+
|
|
19
|
+
That sentence now lives in one file and every screen reads it, so the three cannot
|
|
20
|
+
drift apart again.
|
|
21
|
+
|
|
22
|
+
**The cost is now stated before you press the button, not discovered afterwards.**
|
|
23
|
+
These runs use your own Claude, never ours. On a Max plan that is usage rather than
|
|
24
|
+
a bill, which is exactly why the figure belongs on the screen: on pay as you go it
|
|
25
|
+
is real money, and you should know roughly what a button costs before you press it.
|
|
26
|
+
|
|
27
|
+
## 3.83.5 — the brand page's buttons work, and a run tells you it is working
|
|
28
|
+
|
|
29
|
+
If you use Design Studio, every button on a brand's own page has been dead since
|
|
30
|
+
3.78.0. "Make it" did nothing but load a blank page. So did "Save changes", which
|
|
31
|
+
means any accent, font, name or logo you edited there was thrown away without
|
|
32
|
+
saying so. Nine days, five releases. One piece of broken punctuation in the page's
|
|
33
|
+
script switched all of it off at once, and because a browser discards a script like
|
|
34
|
+
that in silence, nothing on screen ever said so.
|
|
35
|
+
|
|
36
|
+
Both are fixed, and a new check now reads every page the cockpit serves and refuses
|
|
37
|
+
to ship one whose script will not run.
|
|
38
|
+
|
|
39
|
+
A design run also used to tell you nothing at all while it worked. Claude writes the
|
|
40
|
+
whole file in a single answer, so the log stayed empty for ten or twenty minutes,
|
|
41
|
+
and there was no way to tell a working run from a dead one. The sensible thing to do
|
|
42
|
+
was stop it, which was the wrong thing to do. The run now shows a clock, counts the
|
|
43
|
+
passes, and says plainly that nothing will appear until a pass finishes.
|
|
44
|
+
|
|
45
|
+
While reading what gets sent to Claude on those runs, we found the brief was asking
|
|
46
|
+
for two tools that are deliberately switched off for them, and then telling Claude
|
|
47
|
+
in the next line that it had no tools. It no longer does that.
|
|
48
|
+
|
|
49
|
+
**A correction to 3.83.4.** That release said a form field written without its id,
|
|
50
|
+
key and data type "is stored and shows as raw data instead of a rendered field."
|
|
51
|
+
That is wrong, and we tested it after we shipped it rather than before. A field
|
|
52
|
+
without those still renders normally in the form editor. What they actually do is
|
|
53
|
+
tie the answer to the contact record, which is reason enough to set them, and
|
|
54
|
+
`update_form` now says only that.
|
|
55
|
+
|
|
5
56
|
## 3.83.4 — one door for every write, so "protected" cannot quietly stop meaning protected
|
|
6
57
|
|
|
7
58
|
3.83.3 fixed the holes. This closes the shape that kept making them.
|
|
@@ -28,6 +79,11 @@ RENDER rather than only how to send one. A custom question needs the real custom
|
|
|
28
79
|
field's id, key and data type from your account; a field written without them is
|
|
29
80
|
stored and shows as raw data instead of a rendered field.
|
|
30
81
|
|
|
82
|
+
> **Corrected 2026-09-12, in 3.83.5.** The last sentence is wrong. It was tested
|
|
83
|
+
> after this release shipped, not before: a field without those keys still renders
|
|
84
|
+
> normally in the form editor. They tie the answer to the contact record. That is
|
|
85
|
+
> what `update_form` says now.
|
|
86
|
+
|
|
31
87
|
## 3.83.3 — an account you marked protected is now actually protected
|
|
32
88
|
|
|
33
89
|
**Marking a client account protected did not stop fourteen write paths from writing to it.**
|
package/README.md
CHANGED
package/dist/index.js
CHANGED
|
@@ -17177,7 +17177,7 @@ function registerFormBuilderTools(server2, builderClient, publicClient) {
|
|
|
17177
17177
|
);
|
|
17178
17178
|
server2.tool(
|
|
17179
17179
|
"update_form",
|
|
17180
|
-
"Update a form's structure \u2014 fields, labels, conditional logic, auto-responder, email notifications, styling. Use get_form_full first to read the current form, modify the formData object, then pass it here. If name is omitted, the current name is preserved (one extra GET). IMPORTANT \u2014 a
|
|
17180
|
+
"Update a form's structure \u2014 fields, labels, conditional logic, auto-responder, email notifications, styling. Use get_form_full first to read the current form, modify the formData object, then pass it here. If name is omitted, the current name is preserved (one extra GET). IMPORTANT \u2014 a custom-question field should carry the real custom field's `id`, `fieldKey` and `dataType` from THIS account (read them with get_custom_fields; do not invent them), and a standard field (first_name, last_name, email, phone) needs `standard: true` with its `tag`. That binding is what ties the answer to the contact record. A field WITHOUT it still renders in the form editor \u2014 verified live on 2026-09-12, so do not expect a missing binding to show up as a broken field. Copy an existing field from get_form_full rather than composing one from memory; these shapes were reverse-engineered from live UI-built forms.",
|
|
17181
17181
|
{
|
|
17182
17182
|
formId: import_zod44.z.string().describe("The form ID to update."),
|
|
17183
17183
|
formData: import_zod44.z.record(import_zod44.z.string(), import_zod44.z.unknown()).describe("The updated formData object containing form fields, settings, autoResponder config, etc. Fields live at formData.form.fields; each one needs the binding described above or the editor cannot render it."),
|
|
@@ -21368,7 +21368,7 @@ function tokensCss(d, theme) {
|
|
|
21368
21368
|
];
|
|
21369
21369
|
return [...light, "", ...dark].join("\n");
|
|
21370
21370
|
}
|
|
21371
|
-
function composeBrief(d, brief) {
|
|
21371
|
+
function composeBrief(d, brief, packOpts = {}) {
|
|
21372
21372
|
const conversionMode = brief.options.pageMode === "conversion" && brief.conversion;
|
|
21373
21373
|
const { design, antiDefault } = rulesFor(brief.medium, "html", conversionMode ? "conversion" : "standard");
|
|
21374
21374
|
const opts = brief.options;
|
|
@@ -21485,7 +21485,7 @@ ${cleanLogo.slice(0, 6e3)}` : d.logo?.dataUri ? `Logo markup: <img src="${d.logo
|
|
|
21485
21485
|
"## Before you finish",
|
|
21486
21486
|
...checklist.map((c) => `- [ ] ${c}`),
|
|
21487
21487
|
"",
|
|
21488
|
-
`Write the whole file to \`${fileName}\`. Then run lint_design_contract, then verify_design; fix what they name; stop after three passes and report.`
|
|
21488
|
+
...packOpts.tools === false ? [] : [`Write the whole file to \`${fileName}\`. Then run lint_design_contract, then verify_design; fix what they name; stop after three passes and report.`]
|
|
21489
21489
|
].join("\n");
|
|
21490
21490
|
return { fileName, markdown, tokensCss: css2, rules: design, antiDefault, contract: chosenContract, checklist, bannedTells };
|
|
21491
21491
|
}
|
|
@@ -27187,6 +27187,20 @@ var init_run_jobs = __esm({
|
|
|
27187
27187
|
}
|
|
27188
27188
|
});
|
|
27189
27189
|
|
|
27190
|
+
// src/command-os/design/run-expectations.ts
|
|
27191
|
+
var RUN_MAX_PASSES, RUN_PASS_MINUTES, RUN_PASS_COST, RUN_TOTAL_MINUTES, RUN_TOTAL_COST, RUN_EXPECTATION;
|
|
27192
|
+
var init_run_expectations = __esm({
|
|
27193
|
+
"src/command-os/design/run-expectations.ts"() {
|
|
27194
|
+
"use strict";
|
|
27195
|
+
RUN_MAX_PASSES = 3;
|
|
27196
|
+
RUN_PASS_MINUTES = "9 to 13 minutes";
|
|
27197
|
+
RUN_PASS_COST = "$1.25 to $1.75";
|
|
27198
|
+
RUN_TOTAL_MINUTES = "25 to 40 minutes";
|
|
27199
|
+
RUN_TOTAL_COST = "about $5";
|
|
27200
|
+
RUN_EXPECTATION = `Each pass takes ${RUN_PASS_MINUTES} and costs ${RUN_PASS_COST} of your own Claude usage, and a run may take up to ${RUN_MAX_PASSES} passes. Allow ${RUN_TOTAL_MINUTES} and up to ${RUN_TOTAL_COST}.`;
|
|
27201
|
+
}
|
|
27202
|
+
});
|
|
27203
|
+
|
|
27190
27204
|
// src/run-cost.ts
|
|
27191
27205
|
function runCostPath() {
|
|
27192
27206
|
return path18.join(appDataDir(), "command-os-run-cost.jsonl");
|
|
@@ -28591,7 +28605,7 @@ var require_package = __commonJS({
|
|
|
28591
28605
|
"package.json"(exports2, module2) {
|
|
28592
28606
|
module2.exports = {
|
|
28593
28607
|
name: "@elitedcs/ghl-mcp",
|
|
28594
|
-
version: "3.83.
|
|
28608
|
+
version: "3.83.6",
|
|
28595
28609
|
mcpName: "io.github.drjerryrelth/ghl-command",
|
|
28596
28610
|
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.",
|
|
28597
28611
|
main: "dist/index.js",
|
|
@@ -32369,7 +32383,7 @@ ${d.voice.samples.length ? `<h2>Voice</h2><ul>${d.voice.samples.map((s) => `<li>
|
|
|
32369
32383
|
</div>
|
|
32370
32384
|
${opts.screenshot ? `<h2>Their site, as rendered</h2><img class="shot" src="/brand/${encodeURIComponent(d.slug)}/screenshot.png" alt="Screenshot of ${esc5(d.name)}'s website">` : ""}
|
|
32371
32385
|
<h2>Make something in this brand</h2>
|
|
32372
|
-
<p class="small">Runs on <b>your own Claude</b>, on this computer
|
|
32386
|
+
<p class="small">Runs on <b>your own Claude</b>, on this computer, never our key. ${esc5(RUN_EXPECTATION)} The run keeps going if you close this tab. Write the headline and the points you want made, not instructions.</p>
|
|
32373
32387
|
<form id="make"><div class="two">
|
|
32374
32388
|
<div><label for="mk-medium">What</label><select id="mk-medium"><option value="deck">A deck</option><option value="site">A landing page</option><option value="report">A report</option></select></div>
|
|
32375
32389
|
<div><label for="mk-slides">Slides (a deck only)</label><input id="mk-slides" type="number" min="1" max="40" value="6"></div>
|
|
@@ -32397,8 +32411,19 @@ ${d.provenance.source ? `<p class="small">Source: ${esc5(d.provenance.source.url
|
|
|
32397
32411
|
${opts.dir ? `<p class="small">Stored at ${esc5(opts.dir)}</p>` : ""}
|
|
32398
32412
|
${foot(SHELL_JS(opts.seatToken ?? "") + `
|
|
32399
32413
|
(function(){
|
|
32400
|
-
var poll=null,runId=null;
|
|
32401
|
-
|
|
32414
|
+
var poll=null,runId=null,started=0,ticker=null,passes=0;
|
|
32415
|
+
var EXPECT=${scriptJson(RUN_EXPECTATION)};
|
|
32416
|
+
function stopPolling(){if(poll){clearInterval(poll);poll=null}if(ticker){clearInterval(ticker);ticker=null}}
|
|
32417
|
+
function elapsed(ms){var s=Math.floor(ms/1000),m=Math.floor(s/60);s=s%60;return m?(m+"m "+(s<10?"0":"")+s+"s"):(s+"s")}
|
|
32418
|
+
/* The run prints NOTHING while Claude writes - it answers in one go, so the log
|
|
32419
|
+
box sits still for ten minutes or more. Silence used to be indistinguishable
|
|
32420
|
+
from a dead run, and the rational move was to press Stop on a working one
|
|
32421
|
+
(BUG-2026-09-12-03). So the cockpit keeps its own clock: it needs nothing
|
|
32422
|
+
from the child process, and it ticks every second, not every poll. */
|
|
32423
|
+
function tick(){
|
|
32424
|
+
if(!started)return;
|
|
32425
|
+
say("mk-status","Working on your Claude: "+elapsed(Date.now()-started)+" so far"+(passes?(", on pass "+(passes+1)):"")+". Claude writes the whole file in one answer, so nothing appears below until a pass finishes. "+EXPECT+" Closing this tab does not stop it.");
|
|
32426
|
+
}
|
|
32402
32427
|
function show(t){var el=document.getElementById("mk-log");el.hidden=false;el.textContent=t}
|
|
32403
32428
|
document.getElementById("make").addEventListener("submit",async function(e){
|
|
32404
32429
|
e.preventDefault();
|
|
@@ -32415,24 +32440,25 @@ document.getElementById("make").addEventListener("submit",async function(e){
|
|
|
32415
32440
|
var r=await post("/api/design/run",body);
|
|
32416
32441
|
runId=r.id;
|
|
32417
32442
|
document.getElementById("mk-cancel").hidden=false;
|
|
32418
|
-
|
|
32443
|
+
started=Date.now();passes=0;tick();ticker=setInterval(tick,1000);
|
|
32419
32444
|
poll=setInterval(async function(){
|
|
32420
32445
|
var s=await (await fetch("/api/design/run?id="+encodeURIComponent(runId))).json();
|
|
32421
|
-
|
|
32422
|
-
"));
|
|
32446
|
+
var lines=s.tail||[];
|
|
32447
|
+
show(lines.join("\\n"));
|
|
32448
|
+
passes=lines.filter(function(l){return l.trim().slice(0,5)==="pass "}).length;
|
|
32423
32449
|
if(s.status!=="running"){
|
|
32424
32450
|
stopPolling();go.disabled=false;document.getElementById("mk-cancel").hidden=true;
|
|
32425
32451
|
var run=s.run||{};
|
|
32426
32452
|
var design=run.ok?"design PASS on pass "+run.greenAt:"design NOT GREEN";
|
|
32427
|
-
var copy=run.copy?(run.copy.error?" \xB7 copy not graded":" \xB7 copy "+run.copy.verdict+(run.copy.firstBreakdown?" (first breakdown: "+run.copy.firstBreakdown+")"
|
|
32453
|
+
var copy=run.copy?(run.copy.error?" \xB7 copy not graded":" \xB7 copy "+run.copy.verdict+(run.copy.firstBreakdown?" (first breakdown: "+run.copy.firstBreakdown+")":"")):"";
|
|
32428
32454
|
say("mk-status",s.status==="done"?design+copy+" \xB7 "+(run.file||s.outDir):"The run stopped: "+design+copy,s.status==="done"&&run.ok?"ok":"err");
|
|
32429
32455
|
}
|
|
32430
32456
|
},4000);
|
|
32431
|
-
}catch(err){say("mk-status",err.message,"err");go.disabled=false}
|
|
32457
|
+
}catch(err){stopPolling();started=0;say("mk-status",err.message,"err");go.disabled=false}
|
|
32432
32458
|
});
|
|
32433
32459
|
document.getElementById("mk-cancel").addEventListener("click",async function(){
|
|
32434
32460
|
if(!runId)return;
|
|
32435
|
-
try{await post("/api/design/cancel",{id:runId});stopPolling();say("mk-status","Stopped.");document.getElementById("mk-go").disabled=false;this.hidden=true}catch(e){say("mk-status",e.message,"err")}
|
|
32461
|
+
try{await post("/api/design/cancel",{id:runId});stopPolling();started=0;say("mk-status","Stopped.");document.getElementById("mk-go").disabled=false;this.hidden=true}catch(e){say("mk-status",e.message,"err")}
|
|
32436
32462
|
});
|
|
32437
32463
|
})();
|
|
32438
32464
|
document.getElementById("override").addEventListener("submit",async(e)=>{e.preventDefault();const g=(id)=>document.getElementById(id).value.trim();const body={slug:${scriptJson(d.slug)}};if(g("accent"))body.accent=g("accent");if(g("displayFont"))body.displayFont=g("displayFont");if(g("bodyFont"))body.bodyFont=g("bodyFont");if(g("brandName"))body.name=g("brandName");if(g("logoSvg"))body.logoSvg=g("logoSvg");if(Object.keys(body).length===1){say("save-status","Nothing to change.");return}const b=document.getElementById("save");b.disabled=true;try{await post("/api/brand/set",body);say("save-status","Saved.","ok");location.reload()}catch(err){say("save-status",err.message,"err");b.disabled=false}});
|
|
@@ -32442,6 +32468,7 @@ var esc5, STYLE, SHELL_JS, foot;
|
|
|
32442
32468
|
var init_brand_page = __esm({
|
|
32443
32469
|
"src/command-os/design/brand-page.ts"() {
|
|
32444
32470
|
"use strict";
|
|
32471
|
+
init_run_expectations();
|
|
32445
32472
|
init_fonts();
|
|
32446
32473
|
init_cockpit_shell();
|
|
32447
32474
|
init_script_json();
|
|
@@ -36525,7 +36552,7 @@ async function runDashboard(argv) {
|
|
|
36525
36552
|
}
|
|
36526
36553
|
const job = startDesignRun(checked.value);
|
|
36527
36554
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
36528
|
-
res.end(JSON.stringify({ ok: true, id: job.id, outDir: job.outDir, note:
|
|
36555
|
+
res.end(JSON.stringify({ ok: true, id: job.id, outDir: job.outDir, note: `Started on your own Claude. ${RUN_EXPECTATION} Poll /api/design/run?id= for progress.` }));
|
|
36529
36556
|
} catch (e) {
|
|
36530
36557
|
res.writeHead(400, { "Content-Type": "application/json" });
|
|
36531
36558
|
res.end(JSON.stringify({ ok: false, error: e.message }));
|
|
@@ -37283,6 +37310,7 @@ var init_dashboard = __esm({
|
|
|
37283
37310
|
init_claude_cli();
|
|
37284
37311
|
init_token_registry();
|
|
37285
37312
|
init_run_jobs();
|
|
37313
|
+
init_run_expectations();
|
|
37286
37314
|
init_stage_runner();
|
|
37287
37315
|
init_account_rules();
|
|
37288
37316
|
init_claude_call();
|
|
@@ -43689,7 +43717,7 @@ async function runDesign(opts) {
|
|
|
43689
43717
|
const medium = brief.medium;
|
|
43690
43718
|
const outDir = (0, import_node_path7.resolve)(opts.outDir);
|
|
43691
43719
|
(0, import_node_fs7.mkdirSync)(outDir, { recursive: true });
|
|
43692
|
-
const pack = composeBrief(dna, brief);
|
|
43720
|
+
const pack = composeBrief(dna, brief, { tools: false });
|
|
43693
43721
|
const maxPasses = Math.max(1, Math.min(3, opts.maxPasses ?? 3));
|
|
43694
43722
|
const generate = opts.generate ?? claudeGenerate(opts.model ?? DEFAULT_MODEL);
|
|
43695
43723
|
const conversionRun = brief.options.pageMode === "conversion" && brief.conversion ? { brief: brief.conversion, gate: opts.gate ?? "ship" } : void 0;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elitedcs/ghl-mcp",
|
|
3
|
-
"version": "3.83.
|
|
3
|
+
"version": "3.83.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",
|