@elitedcs/ghl-mcp 3.97.2 → 3.97.3
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 +8 -0
- package/README.md +1 -1
- package/dist/brain-function.js +1 -1
- package/dist/index.js +169 -34
- package/guide/guide.html +2 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 3.97.3: A sub-account is never created twice at the same moment, and brain setup names the right fix
|
|
4
|
+
|
|
5
|
+
**Two Creates of one business name at the same moment: the second is refused.** When the same name is being created in the cockpit and in chat, or in two assistant windows on one computer, the second Create now says the sub-account is being created right now and to wait for that to finish. Before, both could go through and make the sub-account twice. This works when every window runs 3.97.3 or later. If GHL Command cannot check this because its shared file is busy, or cannot be read or written, nothing is created and it says to try again in a moment.
|
|
6
|
+
|
|
7
|
+
**Brain setup now gives the right fix for each Anthropic refusal:** a key it does not accept needs a new key; billing, credit or a usage limit keep the same key and say what to fix; a model your workspace cannot use is named, with how to turn on access.
|
|
8
|
+
|
|
9
|
+
The brain's send-or-hold check now has up to 15 seconds to answer (was 6), so the first check after a quiet spell is no longer held just for being slow.
|
|
10
|
+
|
|
3
11
|
## 3.97.2: Two assistant windows share one computer more safely, cockpit runs never wait on a question, and the client brain fails safe
|
|
4
12
|
|
|
5
13
|
**Two assistant windows on one computer keep each other's unrelated changes.** When GHL Command runs in more than one assistant window at once, each one now saves your registered accounts, connections and cockpit board under a shared lock, and merges its change onto what the other saved. Before, one could save over an account the other had just registered. Editing the same details in both windows at once can still replace the earlier change. If the lock stays busy for 5 seconds, nothing is written, and the tool or the card says what was not saved and what to do next; only a few background notes (such as when the desktop icon was offered) are skipped without a message. Most of a cockpit run's own saves (a step's status and record, its job list and its check record) try three times over about 2 to 3 seconds instead. If the lock is still busy, the run's progress says in plain words which one could not be saved and what to do next, and a step that finished still reads as finished; its card shows what was last saved. A sub-account that was created is always reported as created, in chat and in the cockpit, even when its key or its card could not be saved. If the cockpit could not save it, the New Client page lists it as created until you register it with its key or press "Dismiss: I removed this sub-account", and neither the cockpit nor chat will create that name again until then.
|
package/README.md
CHANGED
package/dist/brain-function.js
CHANGED
|
@@ -15982,7 +15982,7 @@ function judgeNote(judgmentId, message, answer) {
|
|
|
15982
15982
|
if (answer.better_message) lines.push(`What would fit now: ${esc2(answer.better_message)}`);
|
|
15983
15983
|
return lines.join("\n");
|
|
15984
15984
|
}
|
|
15985
|
-
var JUDGE_ASK_DEADLINE_MS =
|
|
15985
|
+
var JUDGE_ASK_DEADLINE_MS = 15e3;
|
|
15986
15986
|
var JUDGE_SETTLE_MS = 700;
|
|
15987
15987
|
function workflowAnswer(outcome, judgmentId, kind = "ask", facts = null, rule = DEFAULT_FAIL_RULE, known = {}) {
|
|
15988
15988
|
const hold = (why) => ({ send: "no", why, judgmentId, held: true, task: rule.taskOnHold ? `The brain held a message to this lead. ${why} Check it and send it by hand if it should go.` : null });
|
package/dist/index.js
CHANGED
|
@@ -1269,6 +1269,7 @@ function withFileLock(target, fn, opts = {}) {
|
|
|
1269
1269
|
Atomics.wait(SLEEP, 0, 0, 25);
|
|
1270
1270
|
}
|
|
1271
1271
|
}
|
|
1272
|
+
if (!haveLock && opts.requireLock) throw (opts.busy ?? ((t) => new FileLockBusyError(t)))(target);
|
|
1272
1273
|
holding.set(lockPath, 1);
|
|
1273
1274
|
try {
|
|
1274
1275
|
return fn();
|
|
@@ -36996,14 +36997,24 @@ async function anthropicProbe(fetchFn, key2) {
|
|
|
36996
36997
|
let reached = true;
|
|
36997
36998
|
let lastStatus = 0;
|
|
36998
36999
|
let lastBody = "";
|
|
37000
|
+
let lastModel = "";
|
|
37001
|
+
let modelAnsweredYes = false;
|
|
36999
37002
|
const r = await probeAnthropicKey({
|
|
37000
37003
|
apiKey: key2,
|
|
37001
37004
|
modelIds: BRAIN_CHECKED_MODELS,
|
|
37002
37005
|
fetchImpl: (async (url, init) => {
|
|
37003
37006
|
try {
|
|
37004
37007
|
const res = await fetchFn(String(url), { method: init?.method, headers: init?.headers, body: init?.body, signal: init?.signal ?? void 0, redirect: "error" });
|
|
37008
|
+
if (lastModel && lastStatus >= 200 && lastStatus < 300) modelAnsweredYes = true;
|
|
37005
37009
|
lastStatus = res.status;
|
|
37006
37010
|
lastBody = "";
|
|
37011
|
+
lastModel = (() => {
|
|
37012
|
+
try {
|
|
37013
|
+
return String(JSON.parse(String(init?.body ?? "")).model ?? "");
|
|
37014
|
+
} catch {
|
|
37015
|
+
return "";
|
|
37016
|
+
}
|
|
37017
|
+
})();
|
|
37007
37018
|
return { ok: res.ok, status: res.status, text: async () => {
|
|
37008
37019
|
const b = await res.text();
|
|
37009
37020
|
lastBody = b;
|
|
@@ -37021,18 +37032,25 @@ async function anthropicProbe(fetchFn, key2) {
|
|
|
37021
37032
|
if (r.verdict === "working") return { ok: true };
|
|
37022
37033
|
const unchecked2 = { ok: false, refused: false, error: ANTHROPIC_KEY_UNCHECKED };
|
|
37023
37034
|
if (!reached) return unchecked2;
|
|
37024
|
-
|
|
37025
|
-
|
|
37026
|
-
if (
|
|
37027
|
-
|
|
37035
|
+
if (lastStatus === 401) return { ok: false, refused: true, error: anthropicKeyRefused(lastBody ? r : classifyAnthropicResponse(401, "")) };
|
|
37036
|
+
const said = anthropicError(lastBody);
|
|
37037
|
+
if (!said || lastStatus >= 500) return unchecked2;
|
|
37038
|
+
const orgOrIdentity = said.type === "billing_error" || r.verdict === "identity-linked" || r.verdict === "no-credit" || /usage limit/i.test(said.message);
|
|
37039
|
+
if (orgOrIdentity) return { ok: false, refused: true, error: anthropicKeyRefused(r, said.type) };
|
|
37040
|
+
const modelLevel = Boolean(lastModel) && (said.type === "not_found_error" || said.type === "invalid_request_error" && /\bmodel\b/i.test(said.message) || modelAnsweredYes && said.type === "permission_error");
|
|
37041
|
+
const aboutTheKey = !modelLevel && ANTHROPIC_KEY_ERROR_TYPES.has(said.type);
|
|
37042
|
+
if (aboutTheKey) return { ok: false, refused: true, error: anthropicKeyRefused(r, said.type) };
|
|
37043
|
+
const aboutTheModel = modelLevel;
|
|
37044
|
+
if (aboutTheModel) return { ok: false, refused: true, error: ANTHROPIC_MODEL_UNAVAILABLE(lastModel, false, said.raw), model: { id: lastModel, raw: said.raw } };
|
|
37028
37045
|
return unchecked2;
|
|
37029
37046
|
}
|
|
37030
|
-
function
|
|
37047
|
+
function anthropicError(raw) {
|
|
37031
37048
|
try {
|
|
37032
37049
|
const b = JSON.parse(raw ?? "");
|
|
37033
|
-
|
|
37050
|
+
if (b?.type !== "error" || typeof b.error?.type !== "string") return null;
|
|
37051
|
+
return { type: b.error.type, message: typeof b.error.message === "string" ? b.error.message : "", raw: scrubKeys(String(raw).slice(0, 400)) };
|
|
37034
37052
|
} catch {
|
|
37035
|
-
return
|
|
37053
|
+
return null;
|
|
37036
37054
|
}
|
|
37037
37055
|
}
|
|
37038
37056
|
async function licenceProbe(fetchFn, key2, admission) {
|
|
@@ -37192,9 +37210,10 @@ async function brainSetup(deps, args) {
|
|
|
37192
37210
|
let anthropicNote = null;
|
|
37193
37211
|
if (anthropicGiven) {
|
|
37194
37212
|
const probe = await anthropicProbe(deps.fetch, anthropicGiven);
|
|
37213
|
+
if (!probe.ok && anthropicCurrent && probe.model) return { ok: false, wrote: "nothing", error: ANTHROPIC_MODEL_UNAVAILABLE(probe.model.id, true, probe.model.raw) };
|
|
37195
37214
|
if (!probe.ok && anthropicCurrent && probe.refused) return { ok: false, wrote: "nothing", error: ANTHROPIC_SAVED_KEY_REFUSED(probe.error) };
|
|
37196
37215
|
if (!probe.ok && anthropicCurrent) anthropicNote = "Anthropic could not be reached to check it just now; it is left as it is.";
|
|
37197
|
-
else if (!probe.ok) return { ok: false, wrote: "nothing", error: probe.refused && anthropicSaved && anthropicMarkers.length !== 1 ? `${probe.error} ${ANTHROPIC_UNRECORDED_NOTE}` : probe.error };
|
|
37216
|
+
else if (!probe.ok) return { ok: false, wrote: "nothing", error: probe.refused && !probe.model && anthropicSaved && anthropicMarkers.length !== 1 ? `${probe.error} ${ANTHROPIC_UNRECORDED_NOTE}` : probe.error };
|
|
37198
37217
|
}
|
|
37199
37218
|
const licenceMarker = licenceKeyMarker(licenceKeyHere, admissionHere);
|
|
37200
37219
|
const licenceSaved = secretNames.includes(LICENCE_KEY_SECRET);
|
|
@@ -37592,7 +37611,7 @@ async function clientSettingsReadBack(store2, clientId, c) {
|
|
|
37592
37611
|
const off = Object.keys(c).filter((k) => JSON.stringify(now[k]) !== JSON.stringify(c[k]));
|
|
37593
37612
|
return off.length ? { check, passed: false, detail: `These did not read back as saved: ${off.join(", ")}.` } : { check, passed: true, detail: `Saved and read back: ${Object.keys(c).join(", ")}.` };
|
|
37594
37613
|
}
|
|
37595
|
-
var import_node_crypto15, import_zod58, BRAIN_CHECKED_MODELS, ANTHROPIC_KEY_SECRET, ANTHROPIC_KEY_MARKER_PREFIX, anthropicKeyMarker, shortPrint, scrubKeys, anthropicKeyRefused, ANTHROPIC_KEY_UNCHECKED, ANTHROPIC_SAVED_KEY_REFUSED, ANTHROPIC_UNRECORDED_NOTE, ANTHROPIC_KEY_MALFORMED, LICENCE_KEY_MARKER_PREFIX, licenceKeyMarker, LICENCE_NOT_OS, SETUP_BUSY, LICENCE_NO_ADMISSION, LICENCE_NO_KEY, LICENCE_UNCONFIRMED, BRAIN_TABLES, DEFAULT_REGION, PROBE_CONTACT, SETUP_OVERTAKEN, EDGE_PROBE_CHECK, EDGE_PROBE_CLEANUP, probeRowsSql, BRAIN_MANAGER_CHECK, CLIENT_SETTINGS_CHECK, PURPOSE_RE, FORM_ID_RE;
|
|
37614
|
+
var import_node_crypto15, import_zod58, BRAIN_CHECKED_MODELS, ANTHROPIC_KEY_SECRET, ANTHROPIC_KEY_MARKER_PREFIX, anthropicKeyMarker, shortPrint, scrubKeys, anthropicKeyRefused, ANTHROPIC_KEY_UNCHECKED, ANTHROPIC_SAVED_KEY_REFUSED, ANTHROPIC_UNRECORDED_NOTE, ANTHROPIC_KEY_MALFORMED, ANTHROPIC_KEY_ERROR_TYPES, BRAIN_MODEL_JOB, ANTHROPIC_MODEL_UNAVAILABLE, LICENCE_KEY_MARKER_PREFIX, licenceKeyMarker, LICENCE_NOT_OS, SETUP_BUSY, LICENCE_NO_ADMISSION, LICENCE_NO_KEY, LICENCE_UNCONFIRMED, BRAIN_TABLES, DEFAULT_REGION, PROBE_CONTACT, SETUP_OVERTAKEN, EDGE_PROBE_CHECK, EDGE_PROBE_CLEANUP, probeRowsSql, BRAIN_MANAGER_CHECK, CLIENT_SETTINGS_CHECK, PURPOSE_RE, FORM_ID_RE;
|
|
37596
37615
|
var init_setup = __esm({
|
|
37597
37616
|
"src/command-os/brain/setup.ts"() {
|
|
37598
37617
|
"use strict";
|
|
@@ -37616,11 +37635,22 @@ var init_setup = __esm({
|
|
|
37616
37635
|
anthropicKeyMarker = (key2) => `${ANTHROPIC_KEY_MARKER_PREFIX}${contentHash2(key2).slice(0, 12).toUpperCase()}`;
|
|
37617
37636
|
shortPrint = (key2) => contentHash2(key2).slice(0, 8);
|
|
37618
37637
|
scrubKeys = (t) => t.replace(/sk-ant-[A-Za-z0-9_-]+/g, "[a key]");
|
|
37619
|
-
anthropicKeyRefused = (r
|
|
37638
|
+
anthropicKeyRefused = (r, errorType) => {
|
|
37639
|
+
const said = r.raw ? ` Anthropic said: ${scrubKeys(r.raw)}` : "";
|
|
37640
|
+
const lead = "Anthropic refused this brain-only key, so it was not saved and nothing was created or changed.";
|
|
37641
|
+
if (errorType === "billing_error") return `${lead} Anthropic says the organization's billing stops it, and that applies to every key on the organization, so a new key would answer the same.${said} Fix billing at platform.claude.com under Billing, then run brain_setup again with the same key.`;
|
|
37642
|
+
if (r.verdict === "no-credit") return `${lead} Anthropic says the organization's credit balance is empty, and that applies to every key on it, so a new key would answer the same.${said} Add credit at platform.claude.com under Billing, then run brain_setup again with the same key.`;
|
|
37643
|
+
if (/usage limit/i.test(r.raw ?? "")) return `${lead} Anthropic says a usage limit set on the organization or its workspace stops it, so a new key would answer the same.${said} Raise the limit at platform.claude.com, or wait until it resets, then run brain_setup again with the same key.`;
|
|
37644
|
+
if (r.verdict === "unclear") return `${lead} Anthropic says this key may not do what the brain needs.${said} Create a new key at platform.claude.com under API keys, on a workspace, and run brain_setup again with it.`;
|
|
37645
|
+
return `${lead} ${r.detail}${said} Then run brain_setup again with the new key.`;
|
|
37646
|
+
};
|
|
37620
37647
|
ANTHROPIC_KEY_UNCHECKED = "The brain-only Anthropic key could not be checked with Anthropic just now, so it was not saved and nothing was created or changed. Run brain_setup again in a few minutes.";
|
|
37621
37648
|
ANTHROPIC_SAVED_KEY_REFUSED = (why) => `This is the brain-only Anthropic key already saved inside the project, and Anthropic refuses it now, so the brain cannot write drafts with it. Nothing was changed. ${why.replace(/^Anthropic refused this brain-only key, so it was not saved and nothing was created or changed\. /, "")}`;
|
|
37622
37649
|
ANTHROPIC_UNRECORDED_NOTE = "If this is the key already saved inside the project (its fingerprint was not recorded before 3.95.1), that saved key does not work either, so drafts fail until a working key is saved.";
|
|
37623
37650
|
ANTHROPIC_KEY_MALFORMED = "This brain-only Anthropic key contains a line break or a character no Anthropic key has, so it was not checked or saved and nothing was created or changed. Copy the key again from platform.claude.com under API keys, as one line, and run brain_setup again.";
|
|
37651
|
+
ANTHROPIC_KEY_ERROR_TYPES = /* @__PURE__ */ new Set(["authentication_error", "permission_error", "billing_error"]);
|
|
37652
|
+
BRAIN_MODEL_JOB = { [DEFAULT_BRAIN_MODEL]: "writes the brain's drafts", [JUDGE_MODEL]: "judges send or hold before a workflow message goes out" };
|
|
37653
|
+
ANTHROPIC_MODEL_UNAVAILABLE = (model, saved, raw) => `${saved ? "This is the brain-only Anthropic key already saved inside the project. Anthropic accepts it, but" : "Anthropic accepts this brain-only key, but"} says it cannot use ${model}, the model that ${BRAIN_MODEL_JOB[model] ?? "the brain calls"}. ${saved ? "Nothing was changed." : "So it was not saved and nothing was created or changed."} Anthropic said: ${raw} At platform.claude.com, check that the key's workspace may use ${model} (a new key on the same workspace would answer the same), then run brain_setup again.`;
|
|
37624
37654
|
LICENCE_KEY_MARKER_PREFIX = `${LICENCE_KEY_SECRET}_FP_`;
|
|
37625
37655
|
licenceKeyMarker = (key2, admission) => `${LICENCE_KEY_MARKER_PREFIX}${contentHash2(admission ? `${key2}
|
|
37626
37656
|
${admission}` : key2).slice(0, 12).toUpperCase()}`;
|
|
@@ -53260,7 +53290,7 @@ var require_package = __commonJS({
|
|
|
53260
53290
|
"package.json"(exports2, module2) {
|
|
53261
53291
|
module2.exports = {
|
|
53262
53292
|
name: "@elitedcs/ghl-mcp",
|
|
53263
|
-
version: "3.97.
|
|
53293
|
+
version: "3.97.3",
|
|
53264
53294
|
mcpName: "io.github.drjerryrelth/ghl-command",
|
|
53265
53295
|
description: "GoHighLevel MCP Server for Claude. 250 tools for CRM, automation and marketing, an account-wide workflow audit, live funnel-capture verification and a workflow builder, across client accounts.",
|
|
53266
53296
|
main: "dist/index.js",
|
|
@@ -63840,6 +63870,7 @@ var init_cockpit_routes = __esm({
|
|
|
63840
63870
|
post("/api/connections/disconnect", "agency", BOTH),
|
|
63841
63871
|
post("/api/account-admin-settings", "agency", BOTH),
|
|
63842
63872
|
post("/api/agency-profile", "agency", BOTH),
|
|
63873
|
+
post("/api/created-dismiss", "agency", BOTH),
|
|
63843
63874
|
post("/api/anthropic-key", "agency", BOTH),
|
|
63844
63875
|
post("/api/branding", "agency", BOTH),
|
|
63845
63876
|
post("/api/brand/extract", "agency", BOTH),
|
|
@@ -68687,15 +68718,70 @@ function createdNotYetKept(isKept) {
|
|
|
68687
68718
|
function subAccountNameKey(name2) {
|
|
68688
68719
|
return name2.normalize("NFKC").trim().replace(/\s+/g, " ").toLowerCase();
|
|
68689
68720
|
}
|
|
68721
|
+
function processAlive(pid) {
|
|
68722
|
+
if (pid === process.pid) return true;
|
|
68723
|
+
try {
|
|
68724
|
+
process.kill(pid, 0);
|
|
68725
|
+
return true;
|
|
68726
|
+
} catch (e) {
|
|
68727
|
+
return e.code === "EPERM";
|
|
68728
|
+
}
|
|
68729
|
+
}
|
|
68730
|
+
function liveReservations(now = Date.now()) {
|
|
68731
|
+
let text3 = "";
|
|
68732
|
+
try {
|
|
68733
|
+
text3 = fs50.readFileSync(createdSubAccountsPath(), "utf8");
|
|
68734
|
+
} catch (e) {
|
|
68735
|
+
if (e?.code === "ENOENT") return [];
|
|
68736
|
+
throw e;
|
|
68737
|
+
}
|
|
68738
|
+
const open2 = /* @__PURE__ */ new Map();
|
|
68739
|
+
for (const line4 of text3.split("\n")) {
|
|
68740
|
+
if (!line4.trim()) continue;
|
|
68741
|
+
let raw;
|
|
68742
|
+
try {
|
|
68743
|
+
raw = JSON.parse(line4);
|
|
68744
|
+
} catch {
|
|
68745
|
+
continue;
|
|
68746
|
+
}
|
|
68747
|
+
const r = ReservingLine.safeParse(raw);
|
|
68748
|
+
if (r.success) {
|
|
68749
|
+
open2.set(r.data.id, r.data);
|
|
68750
|
+
continue;
|
|
68751
|
+
}
|
|
68752
|
+
const d = ReleasedLine.safeParse(raw);
|
|
68753
|
+
if (d.success) open2.delete(d.data.released);
|
|
68754
|
+
}
|
|
68755
|
+
return [...open2.values()].filter((r) => now - Date.parse(r.at) < NAME_RESERVATION_STALE_MS && processAlive(r.pid));
|
|
68756
|
+
}
|
|
68690
68757
|
function reserveSubAccountName(name2) {
|
|
68691
68758
|
const key2 = subAccountNameKey(name2);
|
|
68692
68759
|
if (createsInFlight.has(key2)) return null;
|
|
68760
|
+
const id2 = `${process.pid}-${(0, import_crypto4.randomBytes)(6).toString("hex")}`;
|
|
68761
|
+
let reserved = false;
|
|
68762
|
+
try {
|
|
68763
|
+
ensureAppDataDir();
|
|
68764
|
+
reserved = withFileLock(createdSubAccountsPath(), () => {
|
|
68765
|
+
if (liveReservations().some((r) => r.reserving === key2)) return false;
|
|
68766
|
+
fs50.appendFileSync(createdSubAccountsPath(), `${JSON.stringify(ReservingLine.parse({ reserving: key2, id: id2, pid: process.pid, at: (/* @__PURE__ */ new Date()).toISOString() }))}
|
|
68767
|
+
`, { mode: 384 });
|
|
68768
|
+
return true;
|
|
68769
|
+
}, { requireLock: true });
|
|
68770
|
+
} catch {
|
|
68771
|
+
throw new NameReservationUnavailableError(name2);
|
|
68772
|
+
}
|
|
68773
|
+
if (!reserved) return null;
|
|
68693
68774
|
createsInFlight.add(key2);
|
|
68694
68775
|
let released = false;
|
|
68695
|
-
return () => {
|
|
68696
|
-
if (
|
|
68697
|
-
|
|
68698
|
-
|
|
68776
|
+
return (opts) => {
|
|
68777
|
+
if (released) return;
|
|
68778
|
+
released = true;
|
|
68779
|
+
createsInFlight.delete(key2);
|
|
68780
|
+
if (opts?.keepReservation) return;
|
|
68781
|
+
try {
|
|
68782
|
+
fs50.appendFileSync(createdSubAccountsPath(), `${JSON.stringify(ReleasedLine.parse({ released: id2, at: (/* @__PURE__ */ new Date()).toISOString() }))}
|
|
68783
|
+
`, { mode: 384 });
|
|
68784
|
+
} catch {
|
|
68699
68785
|
}
|
|
68700
68786
|
};
|
|
68701
68787
|
}
|
|
@@ -68711,7 +68797,7 @@ function createdNotSavedNote(name2) {
|
|
|
68711
68797
|
function alreadyCreatedRefusal(name2, where = "below") {
|
|
68712
68798
|
return `A sub-account named "${name2}" was already created in GoHighLevel. Register it ${where} instead of creating it again.`;
|
|
68713
68799
|
}
|
|
68714
|
-
var fs50, path50, import_zod89, CreatedRecord, RemovedMarker, JournalLine, createsInFlight, DISMISS_CREATED_LABEL, DISMISSED_CARD_BUSY_NOTE;
|
|
68800
|
+
var fs50, path50, import_zod89, import_crypto4, CreatedRecord, RemovedMarker, JournalLine, createsInFlight, NAME_RESERVATION_STALE_MS, ReservingLine, ReleasedLine, NameReservationUnavailableError, DISMISS_CREATED_LABEL, DISMISSED_CARD_BUSY_NOTE;
|
|
68715
68801
|
var init_created_sub_accounts = __esm({
|
|
68716
68802
|
"src/created-sub-accounts.ts"() {
|
|
68717
68803
|
"use strict";
|
|
@@ -68719,10 +68805,21 @@ var init_created_sub_accounts = __esm({
|
|
|
68719
68805
|
path50 = __toESM(require("path"));
|
|
68720
68806
|
import_zod89 = require("zod");
|
|
68721
68807
|
init_credentials_store();
|
|
68808
|
+
init_file_lock();
|
|
68809
|
+
import_crypto4 = require("crypto");
|
|
68722
68810
|
CreatedRecord = import_zod89.z.object({ locationId: import_zod89.z.string().min(1), name: import_zod89.z.string(), at: import_zod89.z.string() });
|
|
68723
68811
|
RemovedMarker = import_zod89.z.object({ locationId: import_zod89.z.string().min(1), removed: import_zod89.z.literal(true), at: import_zod89.z.string() });
|
|
68724
68812
|
JournalLine = import_zod89.z.union([RemovedMarker, CreatedRecord]);
|
|
68725
68813
|
createsInFlight = /* @__PURE__ */ new Set();
|
|
68814
|
+
NAME_RESERVATION_STALE_MS = 15 * 6e4;
|
|
68815
|
+
ReservingLine = import_zod89.z.object({ reserving: import_zod89.z.string().min(1), id: import_zod89.z.string().min(1), pid: import_zod89.z.number().int(), at: import_zod89.z.string() });
|
|
68816
|
+
ReleasedLine = import_zod89.z.object({ released: import_zod89.z.string().min(1), at: import_zod89.z.string() });
|
|
68817
|
+
NameReservationUnavailableError = class extends Error {
|
|
68818
|
+
constructor(name2) {
|
|
68819
|
+
super(`GHL Command could not check whether "${name2}" is being created in another window (its shared file is busy, or cannot be read or written). Nothing was created; try again in a moment.`);
|
|
68820
|
+
this.name = "NameReservationUnavailableError";
|
|
68821
|
+
}
|
|
68822
|
+
};
|
|
68726
68823
|
DISMISS_CREATED_LABEL = "Dismiss: I removed this sub-account";
|
|
68727
68824
|
DISMISSED_CARD_BUSY_NOTE = "Dismissed. The pending card could not be removed yet because another window was saving; refresh in a moment.";
|
|
68728
68825
|
}
|
|
@@ -69287,20 +69384,30 @@ ${lines.join("\n")}
|
|
|
69287
69384
|
async (args) => {
|
|
69288
69385
|
if (readAgencyProfile().canCreateSubAccounts === false) return { content: [{ type: "text", text: "Create it in GoHighLevel and register it in GHL Command with its key." }], isError: true };
|
|
69289
69386
|
const { isKeptLocationIn: isKeptLocationIn2 } = await Promise.resolve().then(() => (init_dashboard(), dashboard_exports));
|
|
69290
|
-
|
|
69387
|
+
let releaseName;
|
|
69388
|
+
try {
|
|
69389
|
+
releaseName = reserveSubAccountName(args.name);
|
|
69390
|
+
} catch (re) {
|
|
69391
|
+
return { content: [{ type: "text", text: re instanceof Error ? re.message : String(re) }], isError: true };
|
|
69392
|
+
}
|
|
69291
69393
|
if (!releaseName) return { content: [{ type: "text", text: createInProgressRefusal(args.name) }], isError: true };
|
|
69292
69394
|
let outcome;
|
|
69395
|
+
let journaled = false, createdHere2 = false;
|
|
69293
69396
|
try {
|
|
69294
69397
|
const already = createdNotYetKept((id2) => registry2 ? isKeptLocationIn2(registry2, id2) : false).find((r) => sameSubAccountName(r.name, args.name));
|
|
69295
69398
|
if (already) return { content: [{ type: "text", text: alreadyCreatedRefusal(already.name, "with its key (register_location)") }], isError: true };
|
|
69296
69399
|
outcome = await provisionSubAccount(client, registry2, args, { onCreated: (locationId2) => {
|
|
69297
|
-
|
|
69298
|
-
|
|
69299
|
-
|
|
69400
|
+
for (let tries = 0; tries < 3 && !journaled; tries++) {
|
|
69401
|
+
try {
|
|
69402
|
+
recordCreatedSubAccount({ locationId: locationId2, name: args.name, at: (/* @__PURE__ */ new Date()).toISOString() });
|
|
69403
|
+
journaled = true;
|
|
69404
|
+
} catch {
|
|
69405
|
+
}
|
|
69300
69406
|
}
|
|
69407
|
+
createdHere2 = true;
|
|
69301
69408
|
} });
|
|
69302
69409
|
} finally {
|
|
69303
|
-
releaseName();
|
|
69410
|
+
releaseName({ keepReservation: createdHere2 && !journaled });
|
|
69304
69411
|
}
|
|
69305
69412
|
if (!outcome.ok) {
|
|
69306
69413
|
const { newClientFailure: newClientFailure2 } = await Promise.resolve().then(() => (init_dashboard(), dashboard_exports));
|
|
@@ -72708,7 +72815,7 @@ function errLine(msg4) {
|
|
|
72708
72815
|
function preflightWritable() {
|
|
72709
72816
|
try {
|
|
72710
72817
|
const dir = ensureAppDataDir();
|
|
72711
|
-
const probe = path55.join(dir, `.write-probe.${process.pid}.${(0,
|
|
72818
|
+
const probe = path55.join(dir, `.write-probe.${process.pid}.${(0, import_crypto5.randomBytes)(4).toString("hex")}`);
|
|
72712
72819
|
fs55.writeFileSync(probe, "ok");
|
|
72713
72820
|
fs55.unlinkSync(probe);
|
|
72714
72821
|
return true;
|
|
@@ -72996,14 +73103,14 @@ async function runCli(subcommand, argv) {
|
|
|
72996
73103
|
return EXIT_USAGE2;
|
|
72997
73104
|
}
|
|
72998
73105
|
}
|
|
72999
|
-
var import_node_util4, fs55, path55,
|
|
73106
|
+
var import_node_util4, fs55, path55, import_crypto5, EXIT_OK2, EXIT_USAGE2, EXIT_VALIDATION, EXIT_FS, USAGE;
|
|
73000
73107
|
var init_cli = __esm({
|
|
73001
73108
|
"src/cli.ts"() {
|
|
73002
73109
|
"use strict";
|
|
73003
73110
|
import_node_util4 = require("node:util");
|
|
73004
73111
|
fs55 = __toESM(require("fs"));
|
|
73005
73112
|
path55 = __toESM(require("path"));
|
|
73006
|
-
|
|
73113
|
+
import_crypto5 = require("crypto");
|
|
73007
73114
|
init_ghl_client();
|
|
73008
73115
|
init_token_registry();
|
|
73009
73116
|
init_credentials_store();
|
|
@@ -75978,6 +76085,7 @@ __export(dashboard_exports, {
|
|
|
75978
76085
|
CLAUDE_REPAIR_MESSAGES: () => CLAUDE_REPAIR_MESSAGES,
|
|
75979
76086
|
COCKPIT_AFTER_CANCEL_PATHS: () => COCKPIT_AFTER_CANCEL_PATHS,
|
|
75980
76087
|
CONNECT_AGENCY_BEFORE_CREATE: () => CONNECT_AGENCY_BEFORE_CREATE,
|
|
76088
|
+
CREATED_DISMISS_NOT_LISTED: () => CREATED_DISMISS_NOT_LISTED,
|
|
75981
76089
|
DOC_NOT_SAVED: () => DOC_NOT_SAVED,
|
|
75982
76090
|
GATE_BODY: () => GATE_BODY,
|
|
75983
76091
|
HANDOFF_BUILD_DID_NOT_FINISH: () => HANDOFF_BUILD_DID_NOT_FINISH,
|
|
@@ -77128,7 +77236,7 @@ function startRun(locationId2, stage, onDone, meta = {}) {
|
|
|
77128
77236
|
if (stage === 0 || stage === 3) await saveStepJobList(rk, run.lines);
|
|
77129
77237
|
run.ok = outcome.ok;
|
|
77130
77238
|
const before = readCockpitState().clients[rk]?.stages;
|
|
77131
|
-
if (!await saveRetryingBusy(() => onDone(outcome.ok, outcome))) run.lines.push(stage === -1 ? outcome.ok ? CAPABILITY_RUN_NOT_SAVED : CAPABILITY_RUN_STOPPED_NOT_SAVED : outcome.ok ? STEP_STATUS_NOT_SAVED : STOPPED_STEP_STATUS_NOT_SAVED);
|
|
77239
|
+
if (!await saveRetryingBusy(() => onDone(outcome.ok, outcome, run.lines))) run.lines.push(stage === -1 ? outcome.ok ? CAPABILITY_RUN_NOT_SAVED : CAPABILITY_RUN_STOPPED_NOT_SAVED : outcome.ok ? STEP_STATUS_NOT_SAVED : STOPPED_STEP_STATUS_NOT_SAVED);
|
|
77132
77240
|
const after = readCockpitState().clients[rk]?.stages;
|
|
77133
77241
|
if (stage === 4 && secondClient) {
|
|
77134
77242
|
run.lines.push("The client handoff page is prepared for the sub-account's default client only; this client's handoff is not published from here yet.");
|
|
@@ -77203,7 +77311,7 @@ function startRun(locationId2, stage, onDone, meta = {}) {
|
|
|
77203
77311
|
run.ok = false;
|
|
77204
77312
|
run.lines.push(err instanceof Error ? err.message : String(err));
|
|
77205
77313
|
try {
|
|
77206
|
-
if (!await saveRetryingBusy(() => onDone(false))) run.lines.push(stage === -1 ? CAPABILITY_RUN_STOPPED_NOT_SAVED : STOPPED_STEP_STATUS_NOT_SAVED);
|
|
77314
|
+
if (!await saveRetryingBusy(() => onDone(false, void 0, run.lines))) run.lines.push(stage === -1 ? CAPABILITY_RUN_STOPPED_NOT_SAVED : STOPPED_STEP_STATUS_NOT_SAVED);
|
|
77207
77315
|
} catch (e) {
|
|
77208
77316
|
logQuietly(`run ${rk} stage ${stage}: status not saved: ${e instanceof Error ? e.message : String(e)}`);
|
|
77209
77317
|
}
|
|
@@ -80433,7 +80541,7 @@ function renderPage(state,run){if(state.roster)mergeRoster(state.roster);syncRev
|
|
|
80433
80541
|
const p=document.createElement("p");p.textContent=${scriptJson(createdNotSavedNote("{name}"))}.replace("{name}",()=>r.name);shownNotes.push(p.textContent);box.appendChild(p);
|
|
80434
80542
|
/* rc.3: a sub-account the member removed in GoHighLevel (or will not use) is cleared here for good. */
|
|
80435
80543
|
const dismiss=document.createElement("button");dismiss.type="button";dismiss.className="cds-btn ghost nc-created-dismiss";dismiss.textContent=${scriptJson(DISMISS_CREATED_LABEL)};
|
|
80436
|
-
dismiss.onclick=async()=>{dismiss.disabled=true;const j=await postJSON("/api/pending-remove",{locationId:r.locationId});if(j){if(j.note)alertFor(r.locationId,j.note,r.name);load()}else dismiss.disabled=false};box.appendChild(dismiss);m.appendChild(box)}
|
|
80544
|
+
dismiss.onclick=async()=>{dismiss.disabled=true;const j=await postJSON(PLAN==="pro"?"/api/created-dismiss":"/api/pending-remove",{locationId:r.locationId});if(j){if(j.note)alertFor(r.locationId,j.note,r.name);load()}else dismiss.disabled=false};box.appendChild(dismiss);m.appendChild(box)}
|
|
80437
80545
|
if(!c0&®isterNotice&&!shownNotes.includes(registerNotice)){const note=document.createElement("p");note.className="cds-callout warn";note.setAttribute("role","alert");note.textContent=registerNotice;m.appendChild(note)}
|
|
80438
80546
|
let waiting=null;{const wc=bsWaitingCard(state,run,c0?c0.locationId:"");if(wc&&(!c0||bsWhere(c0,state,run).step<3)){m.appendChild(wc);waiting=wc}}
|
|
80439
80547
|
if(!c0){
|
|
@@ -82002,11 +82110,11 @@ async function runDashboard(argv) {
|
|
|
82002
82110
|
const st = readCockpitState();
|
|
82003
82111
|
const approvedAt = st.buildApproved?.[locationId2];
|
|
82004
82112
|
assertCockpitLicenseHolds();
|
|
82005
|
-
startRun(locationId2, -1, (ok, outcome) => {
|
|
82113
|
+
startRun(locationId2, -1, (ok, outcome, runLines) => {
|
|
82006
82114
|
cockpitCache.invalidate(locationId2, [`capability:${cap2.key}`]);
|
|
82007
82115
|
try {
|
|
82008
82116
|
const now = readCockpitState();
|
|
82009
|
-
const last =
|
|
82117
|
+
const last = { lines: runLines ?? [] };
|
|
82010
82118
|
const runs = { ...now.capabilityRuns ?? {} };
|
|
82011
82119
|
const done = (last?.lines ?? []).filter((l) => /^Done[:.]/.test(l)).pop() ?? "";
|
|
82012
82120
|
runs[`${locationId2}:${cap2.key}`] = {
|
|
@@ -82380,7 +82488,7 @@ ${opportunityNoteFailure?.detail ?? ""}`);
|
|
|
82380
82488
|
const proClient = gate3.plan === "pro" ? new URL(req.url || "/", "http://x").searchParams.get("locationId") ?? "" : void 0;
|
|
82381
82489
|
const st = proClient === void 0 ? readCockpitState() : sliceStateFor(readCockpitState(), proClient);
|
|
82382
82490
|
const rosterNow = proClient === void 0 ? currentRoster() : currentRoster().filter((c) => c.locationId === proClient);
|
|
82383
|
-
const createdNotKept = createdNotYetKept((id2) => isKeptLocationIn(registry2, id2))
|
|
82491
|
+
const createdNotKept = createdNotYetKept((id2) => isKeptLocationIn(registry2, id2));
|
|
82384
82492
|
const liveClaims = {};
|
|
82385
82493
|
for (const [loc, c] of Object.entries(st.clients ?? {})) {
|
|
82386
82494
|
liveClaims[loc] = liveClaim(c.stages?.[6] === "done", st.goLive?.[loc], st.outcomes?.[`${loc}:3`]?.at);
|
|
@@ -83863,12 +83971,19 @@ ${opportunityNoteFailure?.detail ?? ""}`);
|
|
|
83863
83971
|
if (req.method === "POST" && url === "/api/new-client") {
|
|
83864
83972
|
readBody(req, res, 2e4, async (body) => {
|
|
83865
83973
|
let releaseName = null;
|
|
83974
|
+
let createdNotJournaled = false;
|
|
83866
83975
|
try {
|
|
83867
83976
|
const args = JSON.parse(body);
|
|
83868
83977
|
if (!args.name || typeof args.name !== "string") throw new Error("business name is required");
|
|
83869
83978
|
if (readAgencyProfile().canCreateSubAccounts === false) throw new Error(CREATE_PLAN_REFUSAL);
|
|
83870
83979
|
args.industry = canonicalIndustry(args.industry);
|
|
83871
|
-
|
|
83980
|
+
try {
|
|
83981
|
+
releaseName = reserveSubAccountName(args.name);
|
|
83982
|
+
} catch (re) {
|
|
83983
|
+
res.writeHead(503, { "Content-Type": "application/json" });
|
|
83984
|
+
res.end(JSON.stringify({ ok: false, error: re instanceof Error ? re.message : String(re) }));
|
|
83985
|
+
return;
|
|
83986
|
+
}
|
|
83872
83987
|
if (!releaseName) {
|
|
83873
83988
|
res.writeHead(409, { "Content-Type": "application/json" });
|
|
83874
83989
|
res.end(JSON.stringify({ ok: false, error: createInProgressRefusal(args.name) }));
|
|
@@ -83906,6 +84021,7 @@ ${opportunityNoteFailure?.detail ?? ""}`);
|
|
|
83906
84021
|
}
|
|
83907
84022
|
const dummy = new GHLClient({ apiKey: registry2.getAgencyKey() || "unset" });
|
|
83908
84023
|
let journaled = false;
|
|
84024
|
+
createdNotJournaled = false;
|
|
83909
84025
|
const outcome = await provisionSubAccount(dummy, registry2, {
|
|
83910
84026
|
name: args.name,
|
|
83911
84027
|
snapshot_id: args.snapshot_id || void 0,
|
|
@@ -83918,9 +84034,11 @@ ${opportunityNoteFailure?.detail ?? ""}`);
|
|
|
83918
84034
|
timezone: args.timezone || void 0
|
|
83919
84035
|
}, { onCreated: (locationId2) => {
|
|
83920
84036
|
journaled = journalCreatedSubAccount(locationId2, args.name);
|
|
84037
|
+
createdNotJournaled = !journaled;
|
|
83921
84038
|
} });
|
|
83922
84039
|
if (!outcome.ok) throw new Error(outcome.error);
|
|
83923
|
-
if (!journaled) journalCreatedSubAccount(outcome.locationId, args.name);
|
|
84040
|
+
if (!journaled) journaled = journalCreatedSubAccount(outcome.locationId, args.name);
|
|
84041
|
+
createdNotJournaled = !journaled;
|
|
83924
84042
|
let notSaved = false;
|
|
83925
84043
|
let cardNotSaved = false;
|
|
83926
84044
|
try {
|
|
@@ -83960,7 +84078,7 @@ ${opportunityNoteFailure?.detail ?? ""}`);
|
|
|
83960
84078
|
res.writeHead(400, { "Content-Type": "application/json" });
|
|
83961
84079
|
res.end(JSON.stringify({ ok: false, error: newClientFailure(e) + (refused ? savedNo ? " Command OS saved No for creating sub-accounts." : " Command OS could not save No; update Your agency when storage is available." : ""), ...refused ? { canCreateSubAccounts: false } : {} }));
|
|
83962
84080
|
} finally {
|
|
83963
|
-
releaseName?.();
|
|
84081
|
+
releaseName?.({ keepReservation: createdNotJournaled });
|
|
83964
84082
|
}
|
|
83965
84083
|
});
|
|
83966
84084
|
return;
|
|
@@ -84019,6 +84137,22 @@ ${opportunityNoteFailure?.detail ?? ""}`);
|
|
|
84019
84137
|
}, "That request is too large.", true);
|
|
84020
84138
|
return;
|
|
84021
84139
|
}
|
|
84140
|
+
if (req.method === "POST" && url === "/api/created-dismiss") {
|
|
84141
|
+
readBody(req, res, 1e4, (body) => {
|
|
84142
|
+
try {
|
|
84143
|
+
const { locationId: locationId2 } = JSON.parse(body || "{}");
|
|
84144
|
+
const listed = typeof locationId2 === "string" && locationId2 !== "" && createdNotYetKept((id2) => isKeptLocationIn(registry2, id2)).some((r) => r.locationId === locationId2);
|
|
84145
|
+
if (!listed) throw new Error(CREATED_DISMISS_NOT_LISTED);
|
|
84146
|
+
recordRemovedSubAccount(locationId2);
|
|
84147
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
84148
|
+
res.end(JSON.stringify({ ok: true }));
|
|
84149
|
+
} catch (e) {
|
|
84150
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
84151
|
+
res.end(JSON.stringify({ ok: false, error: e instanceof Error && e.message === CREATED_DISMISS_NOT_LISTED ? CREATED_DISMISS_NOT_LISTED : "That could not be dismissed. Refresh the page and try again." }));
|
|
84152
|
+
}
|
|
84153
|
+
});
|
|
84154
|
+
return;
|
|
84155
|
+
}
|
|
84022
84156
|
if (req.method === "POST" && url === "/api/pending-remove") {
|
|
84023
84157
|
readBody(req, res, 1e4, (body) => {
|
|
84024
84158
|
try {
|
|
@@ -84317,7 +84451,7 @@ ${opportunityNoteFailure?.detail ?? ""}`);
|
|
|
84317
84451
|
process.on("SIGTERM", stop3);
|
|
84318
84452
|
});
|
|
84319
84453
|
}
|
|
84320
|
-
var import_zod92, import_node_util6, fs57, path57, http2, import_child_process4, import_child_process5, osmod, VIEWER_COCKPIT_MSG, STAGES2, ACCOUNT_TYPES, SECOND_CLIENT_NO_INTAKE, stateRecovery, lastRead, READ_BASE, isMap, same2, stateReadFs, stateSaveFs, CHOOSE_WHAT_THEY_BOUGHT, CHOOSE_BEFORE_BUILD, TEXT_SNIPPETS_LOCKED, SCOPE_REFUSAL, APPROVAL_WITHDRAWN_FOR_STAFF, APPROVAL_WITHDRAWN_NO_TEAM, HANDOFF_WORKFLOW, TEXTING_CONFIRM_HEAD, NOTHING_BOUGHT, NOTHING_BUILT_FROM_TICKS, ASSESSMENT_CHANGED_SINCE_APPROVAL, APPROVAL_WITHDRAWN_FOR_ASSESSMENT, APPROVE_NEEDS_INTAKE, APPROVE_NEEDS_BRIEF, INDUSTRY_FIRST, NOTHING_TICKED_IN_PLAN, SCOPE_CHANGED_SINCE_APPROVAL, APPROVAL_WITHDRAWN_FOR_SCOPE, BUILD_WHAT_WAS_WAITING, BUILD_KEEPS_WHAT_WAS_REMOVED, BUILD_ADDS_AND_KEEPS, BRIEF_BEHIND_BOUGHT, BUILD_BEHIND_BOUGHT, APPROVE_WAITS_FOR_REVIEW_UPDATE, ALWAYS_BUILT_NOTE, APPROVE_NEEDS_FRESH_REVIEW, BUILD_LOCK_BUSY, APPROVE_WAITS_FOR_BUILD, REVOKE_WAITS_FOR_BUILD, HANDOFF_WAITS_ON_REBUILD, HANDOFF_BUILD_DID_NOT_FINISH, HANDOFF_VERIFY_DID_NOT_PASS, HANDOFF_NEEDS_APPROVAL, HANDOFF_PAGE_NOT_APPROVED, HANDOFF_PAGE_BEHIND_BOUGHT, INACTIVE, OFF_LICENSE, LICENSE_RECHECK_MS, activeRun, runStarting, buildOneTime, oneTimeView, runForTests, handoffEmailPreviews, handoffsSending, REVOKE_WAITS_FOR_HANDOFF, busyRetry, UPGRADE_MSG, DOC_EXTS, DOC_MAX_BYTES, queueScanWatch, queueScanOnce, ASSESSMENT_PIPELINE_MISSING, GATE_BODY, GATE_BODY_MAX, DOC_NOT_SAVED, NO_KEY_ON_FILE, PARKED_NOT_BUILT, NEEDS_AGENCY_KEY, CONNECT_AGENCY_BEFORE_CREATE, NEW_CLIENT_CARD_NOT_SAVED, BUILD_RECORD_NOT_SAVED, VERIFY_JOBS_NOT_SAVED, STEP_JOBS_NOT_SAVED, VERIFY_ATTEMPT_NOT_SAVED, STEP_STATUS_NOT_SAVED, STOPPED_STEP_STATUS_NOT_SAVED, OUTCOME_NOT_SAVED, CAPABILITY_RUN_NOT_SAVED, CAPABILITY_RUN_STOPPED_NOT_SAVED, INTAKE_UNREADABLE, INTAKE_READ_TRANSIENT, INTAKE_REFRESH_RETRYING, ACCOUNT_CHANGED_DURING_READ, commitWithoutRead, SNAPSHOTS_CHANGED_DURING_READ, IntakeColdError, INTAKE_ANSWERS_MISSING, INTAKE_READ_FAILED, RUN_IN_PROGRESS, BRIEF_NEEDS_INTAKE2, BUSINESS_TYPE_FOR_INDUSTRY, INTAKE_FORM_REQUIRED, INTAKE_CONNECT_REQUIRED, INTAKE_OWN_MESSAGES, currentRosterRef, staffGateRef, BOARD_ROUTES, BOARD_TITLES, SCREEN_HEADS, NO_SUCH_PAGE, CHOOSE_CLIENT, ONE_CLIENT_ONLY, anthropicKeyWrites, SEAT_TOKEN_HEADER, notSignedInMsg, NOT_SIGNED_IN_MSG, SEAT_GUARDED_READS, COCKPIT_AFTER_CANCEL_PATHS, licenceRefusedHooks, ConnectionRequest, DisconnectRequest, ReportBrandSaveRequest;
|
|
84454
|
+
var import_zod92, import_node_util6, fs57, path57, http2, import_child_process4, import_child_process5, osmod, VIEWER_COCKPIT_MSG, STAGES2, ACCOUNT_TYPES, SECOND_CLIENT_NO_INTAKE, stateRecovery, lastRead, READ_BASE, isMap, same2, stateReadFs, stateSaveFs, CHOOSE_WHAT_THEY_BOUGHT, CHOOSE_BEFORE_BUILD, TEXT_SNIPPETS_LOCKED, SCOPE_REFUSAL, APPROVAL_WITHDRAWN_FOR_STAFF, APPROVAL_WITHDRAWN_NO_TEAM, HANDOFF_WORKFLOW, TEXTING_CONFIRM_HEAD, NOTHING_BOUGHT, NOTHING_BUILT_FROM_TICKS, ASSESSMENT_CHANGED_SINCE_APPROVAL, APPROVAL_WITHDRAWN_FOR_ASSESSMENT, APPROVE_NEEDS_INTAKE, APPROVE_NEEDS_BRIEF, INDUSTRY_FIRST, NOTHING_TICKED_IN_PLAN, SCOPE_CHANGED_SINCE_APPROVAL, APPROVAL_WITHDRAWN_FOR_SCOPE, BUILD_WHAT_WAS_WAITING, BUILD_KEEPS_WHAT_WAS_REMOVED, BUILD_ADDS_AND_KEEPS, BRIEF_BEHIND_BOUGHT, BUILD_BEHIND_BOUGHT, APPROVE_WAITS_FOR_REVIEW_UPDATE, ALWAYS_BUILT_NOTE, APPROVE_NEEDS_FRESH_REVIEW, BUILD_LOCK_BUSY, APPROVE_WAITS_FOR_BUILD, REVOKE_WAITS_FOR_BUILD, HANDOFF_WAITS_ON_REBUILD, HANDOFF_BUILD_DID_NOT_FINISH, HANDOFF_VERIFY_DID_NOT_PASS, HANDOFF_NEEDS_APPROVAL, HANDOFF_PAGE_NOT_APPROVED, HANDOFF_PAGE_BEHIND_BOUGHT, INACTIVE, OFF_LICENSE, LICENSE_RECHECK_MS, activeRun, runStarting, buildOneTime, oneTimeView, runForTests, handoffEmailPreviews, handoffsSending, REVOKE_WAITS_FOR_HANDOFF, busyRetry, UPGRADE_MSG, DOC_EXTS, DOC_MAX_BYTES, queueScanWatch, queueScanOnce, ASSESSMENT_PIPELINE_MISSING, GATE_BODY, GATE_BODY_MAX, DOC_NOT_SAVED, NO_KEY_ON_FILE, PARKED_NOT_BUILT, NEEDS_AGENCY_KEY, CONNECT_AGENCY_BEFORE_CREATE, CREATED_DISMISS_NOT_LISTED, NEW_CLIENT_CARD_NOT_SAVED, BUILD_RECORD_NOT_SAVED, VERIFY_JOBS_NOT_SAVED, STEP_JOBS_NOT_SAVED, VERIFY_ATTEMPT_NOT_SAVED, STEP_STATUS_NOT_SAVED, STOPPED_STEP_STATUS_NOT_SAVED, OUTCOME_NOT_SAVED, CAPABILITY_RUN_NOT_SAVED, CAPABILITY_RUN_STOPPED_NOT_SAVED, INTAKE_UNREADABLE, INTAKE_READ_TRANSIENT, INTAKE_REFRESH_RETRYING, ACCOUNT_CHANGED_DURING_READ, commitWithoutRead, SNAPSHOTS_CHANGED_DURING_READ, IntakeColdError, INTAKE_ANSWERS_MISSING, INTAKE_READ_FAILED, RUN_IN_PROGRESS, BRIEF_NEEDS_INTAKE2, BUSINESS_TYPE_FOR_INDUSTRY, INTAKE_FORM_REQUIRED, INTAKE_CONNECT_REQUIRED, INTAKE_OWN_MESSAGES, currentRosterRef, staffGateRef, BOARD_ROUTES, BOARD_TITLES, SCREEN_HEADS, NO_SUCH_PAGE, CHOOSE_CLIENT, ONE_CLIENT_ONLY, anthropicKeyWrites, SEAT_TOKEN_HEADER, notSignedInMsg, NOT_SIGNED_IN_MSG, SEAT_GUARDED_READS, COCKPIT_AFTER_CANCEL_PATHS, licenceRefusedHooks, ConnectionRequest, DisconnectRequest, ReportBrandSaveRequest;
|
|
84321
84455
|
var init_dashboard = __esm({
|
|
84322
84456
|
"src/dashboard.ts"() {
|
|
84323
84457
|
"use strict";
|
|
@@ -84559,6 +84693,7 @@ var init_dashboard = __esm({
|
|
|
84559
84693
|
PARKED_NOT_BUILT = "Template and hidden accounts are not built from Command OS. Change the account's type to Client first.";
|
|
84560
84694
|
NEEDS_AGENCY_KEY = `Your agency is not connected yet. ${AGENCY_KEY_HOW}`;
|
|
84561
84695
|
CONNECT_AGENCY_BEFORE_CREATE = `Connect your agency key before creating a sub-account. ${AGENCY_KEY_HOW}`;
|
|
84696
|
+
CREATED_DISMISS_NOT_LISTED = "That sub-account is not listed as created here. Refresh the page.";
|
|
84562
84697
|
NEW_CLIENT_CARD_NOT_SAVED = "The sub-account was created, but its card could not be saved yet: refresh in a moment.";
|
|
84563
84698
|
BUILD_RECORD_NOT_SAVED = "This run finished and may have changed the account; only its record could not be saved because another window was saving. Check the account before running it again.";
|
|
84564
84699
|
VERIFY_JOBS_NOT_SAVED = "The job list from this check could not be saved because another window was saving; run it again.";
|
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: 2f739a97ecc1ad29 -->
|
|
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
|
|
@@ -1715,7 +1715,7 @@ Show me the first ten rows in the chat so I can sanity check them before you wri
|
|
|
1715
1715
|
<li><b>Done means:</b> you asked "Which account am I in?" and Claude answered with the right account name. That is the whole test.</li>
|
|
1716
1716
|
<li>Step 2 is part of the install, not an extra. Until it is done, Claude will keep reminding you the install is one step from finished. Nothing will pretend to be complete when it is not.</li>
|
|
1717
1717
|
<li>If anything says "not working" afterward, say "run a health check": it tests every connection and tells you in plain English what, if anything, needs attention. Its first check, Host, names the assistant GHL Command is running in, and its License plan line says which plan this session runs on.</li>
|
|
1718
|
-
<li>You can have GHL Command open in more than one assistant window on the same computer. Each window saves your registered accounts, connections and cockpit board under a shared lock and adds its change to what the other saved. Saves keep unrelated changes made by another window. Editing the same details in both windows at once can still replace the earlier change. If the other window still holds the save after 5 seconds, the waiting save does not happen; if your plan includes the cockpit, some of a cockpit run's own saves try up to three times over a few seconds instead. When a tool, a run or the cockpit says something was not saved, follow what it tells you to do, such as running it again or checking the account and refreshing. A few background notes are skipped without a message. A sub-account that was created is still reported as created, even if its key or its card could not be saved yet. Do not create it again; register it with its key. If neither its key nor its card was saved and your plan includes the cockpit, its New Client page may show it as "Created:" followed by its name when no client is selected, and while it shows, the cockpit and chat refuse to create that name again. To clear it, register it with its key or, if you already removed it in GoHighLevel, press "Dismiss: I removed this sub-account". Dismiss does not delete the sub-account or check that you removed it. If nothing shows, check GoHighLevel before you create it again.</li>
|
|
1718
|
+
<li>You can have GHL Command open in more than one assistant window on the same computer. Each window saves your registered accounts, connections and cockpit board under a shared lock and adds its change to what the other saved. Saves keep unrelated changes made by another window. Editing the same details in both windows at once can still replace the earlier change. If the other window still holds the save after 5 seconds, the waiting save does not happen; if your plan includes the cockpit, some of a cockpit run's own saves try up to three times over a few seconds instead. When a tool, a run or the cockpit says something was not saved, follow what it tells you to do, such as running it again or checking the account and refreshing. A few background notes are skipped without a message. A sub-account that was created is still reported as created, even if its key or its card could not be saved yet. Do not create it again; register it with its key. If neither its key nor its card was saved and your plan includes the cockpit, its New Client page may show it as "Created:" followed by its name when no client is selected, and while it shows, the cockpit and chat refuse to create that name again. To clear it, register it with its key or, if you already removed it in GoHighLevel, press "Dismiss: I removed this sub-account". Dismiss does not delete the sub-account or check that you removed it. If nothing shows, check GoHighLevel before you create it again. If the same business name is already being created in another window on this computer, or, if your plan includes the cockpit, in the cockpit while you ask in chat, the second attempt is refused before GoHighLevel is asked and nothing is created: wait for the first to finish; do not create it again. This check works only when every open window, and the cockpit, run 3.97.3 or later; one started before you updated does not take part, so after updating, restart your computer before you create a sub-account. If GHL Command cannot check this because its shared file is busy, or cannot be read or written, it also creates nothing and asks you to try again in a moment.</li>
|
|
1719
1719
|
<li>Stuck at any point? Email support@ghlcommand.com and a human answers.</li>
|
|
1720
1720
|
</ul>
|
|
1721
1721
|
</div>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elitedcs/ghl-mcp",
|
|
3
|
-
"version": "3.97.
|
|
3
|
+
"version": "3.97.3",
|
|
4
4
|
"mcpName": "io.github.drjerryrelth/ghl-command",
|
|
5
5
|
"description": "GoHighLevel MCP Server for Claude. 250 tools for CRM, automation and marketing, an account-wide workflow audit, live funnel-capture verification and a workflow builder, across client accounts.",
|
|
6
6
|
"main": "dist/index.js",
|