@elitedcs/ghl-mcp 3.83.1 → 3.83.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 +42 -0
- package/README.md +1 -1
- package/dist/index.js +200 -96
- package/guide/guide.html +4 -3
- package/package.json +2 -6
- package/src/command-os/design/rules/check-static.mjs +0 -238
- package/src/command-os/design/rules/fixtures/claude-default.html +0 -55
- package/src/command-os/design/rules/fixtures/external-css.html +0 -7
- package/src/command-os/design/rules/fixtures/generator-run-1.html +0 -31
- package/src/command-os/design/rules/fixtures/house-clean.html +0 -59
package/dist/index.js
CHANGED
|
@@ -241,7 +241,7 @@ function foldCredentialsIntoEnv(creds, env = process.env) {
|
|
|
241
241
|
];
|
|
242
242
|
for (const [credKey, envKey] of mappings) {
|
|
243
243
|
const value = creds[credKey];
|
|
244
|
-
if (value && !env[envKey]) {
|
|
244
|
+
if (typeof value === "string" && value && !env[envKey]) {
|
|
245
245
|
env[envKey] = value;
|
|
246
246
|
}
|
|
247
247
|
}
|
|
@@ -740,13 +740,13 @@ var init_token_registry = __esm({
|
|
|
740
740
|
name: import_zod2.z.string().optional()
|
|
741
741
|
});
|
|
742
742
|
TokenRegistryDataSchema = import_zod2.z.object({
|
|
743
|
-
tokens: import_zod2.z.record(LocationTokenSchema),
|
|
743
|
+
tokens: import_zod2.z.record(import_zod2.z.string(), LocationTokenSchema),
|
|
744
744
|
agencyKey: import_zod2.z.string().optional(),
|
|
745
745
|
// "Home" Firebase — the company the install was set up for (credentials.json).
|
|
746
746
|
firebase: FirebaseConfigSchema.optional(),
|
|
747
747
|
// Additional companies' Firebase auth, keyed by GHL companyId. Lets ONE
|
|
748
748
|
// process operate the workflow builder across multiple clients' GHL accounts.
|
|
749
|
-
firebaseByCompany: import_zod2.z.record(CompanyFirebaseSchema).optional()
|
|
749
|
+
firebaseByCompany: import_zod2.z.record(import_zod2.z.string(), CompanyFirebaseSchema).optional()
|
|
750
750
|
});
|
|
751
751
|
TokenRegistry = class _TokenRegistry {
|
|
752
752
|
data;
|
|
@@ -1256,7 +1256,7 @@ var init_trigger_schemas = __esm({
|
|
|
1256
1256
|
masterType: import_zod3.z.string().optional(),
|
|
1257
1257
|
name: import_zod3.z.string().optional(),
|
|
1258
1258
|
actions: import_zod3.z.array(TriggerActionSchema).optional(),
|
|
1259
|
-
schedule_config: import_zod3.z.record(import_zod3.z.unknown()).optional(),
|
|
1259
|
+
schedule_config: import_zod3.z.record(import_zod3.z.string(), import_zod3.z.unknown()).optional(),
|
|
1260
1260
|
date_updated: import_zod3.z.string().optional()
|
|
1261
1261
|
}).passthrough();
|
|
1262
1262
|
ContactTagTriggerSchema = TriggerCommonSchema.extend({
|
|
@@ -1547,7 +1547,7 @@ var init_trigger_schemas = __esm({
|
|
|
1547
1547
|
});
|
|
1548
1548
|
UnknownTriggerSchema = TriggerCommonSchema.extend({
|
|
1549
1549
|
type: import_zod3.z.string(),
|
|
1550
|
-
conditions: import_zod3.z.array(import_zod3.z.record(import_zod3.z.unknown())).optional()
|
|
1550
|
+
conditions: import_zod3.z.array(import_zod3.z.record(import_zod3.z.string(), import_zod3.z.unknown())).optional()
|
|
1551
1551
|
});
|
|
1552
1552
|
WorkflowTriggerSchema = import_zod3.z.union([
|
|
1553
1553
|
ContactTagTriggerSchema,
|
|
@@ -3883,7 +3883,8 @@ function annotateListResponse(raw, listKey, limit, nextHint) {
|
|
|
3883
3883
|
};
|
|
3884
3884
|
}
|
|
3885
3885
|
function safeTool(server2, name, description, schema, handler) {
|
|
3886
|
-
|
|
3886
|
+
const toolServer = server2;
|
|
3887
|
+
toolServer.tool(name, description, schema, async (args) => {
|
|
3887
3888
|
try {
|
|
3888
3889
|
return jsonResponse(await handler(args));
|
|
3889
3890
|
} catch (error) {
|
|
@@ -5090,7 +5091,7 @@ function registerCalendarTools(server2, client) {
|
|
|
5090
5091
|
slug: import_zod12.z.string().optional().describe("Calendar slug"),
|
|
5091
5092
|
widgetSlug: import_zod12.z.string().optional().describe("Widget slug for embedding"),
|
|
5092
5093
|
calendarType: import_zod12.z.string().optional().describe("Calendar type"),
|
|
5093
|
-
teamMembers: import_zod12.z.array(import_zod12.z.record(import_zod12.z.unknown())).optional().describe("Array of team member objects"),
|
|
5094
|
+
teamMembers: import_zod12.z.array(import_zod12.z.record(import_zod12.z.string(), import_zod12.z.unknown())).optional().describe("Array of team member objects"),
|
|
5094
5095
|
eventTitle: import_zod12.z.string().optional().describe("Default event title"),
|
|
5095
5096
|
eventColor: import_zod12.z.string().optional().describe("Event color hex code"),
|
|
5096
5097
|
slotDuration: import_zod12.z.number().optional().describe("Slot duration in minutes"),
|
|
@@ -5386,7 +5387,7 @@ function registerLocationTools(server2, client) {
|
|
|
5386
5387
|
timezone: import_zod13.z.string().optional().describe("IANA timezone (e.g., 'America/New_York')."),
|
|
5387
5388
|
phone: import_zod13.z.string().optional().describe("Phone number."),
|
|
5388
5389
|
email: import_zod13.z.string().optional().describe("Email address."),
|
|
5389
|
-
settings: import_zod13.z.record(import_zod13.z.unknown()).optional().describe("Location settings object with key-value pairs."),
|
|
5390
|
+
settings: import_zod13.z.record(import_zod13.z.string(), import_zod13.z.unknown()).optional().describe("Location settings object with key-value pairs."),
|
|
5390
5391
|
overwriteBusinessInfo: import_zod13.z.boolean().optional().describe(
|
|
5391
5392
|
"Allow a CHANGE to business details on a sub-account that already has a registered A2P brand. Off by default. Only needed when the account has a brandId AND you are changing (not filling) one of name/address/city/state/postalCode/country/phone/website/email."
|
|
5392
5393
|
)
|
|
@@ -6726,7 +6727,7 @@ function registerUserTools(server2, client, registry2) {
|
|
|
6726
6727
|
role: import_zod22.z.enum(["admin", "user"]).describe("GHL role on this sub-account. 'admin' = full settings access; 'user' = day-to-day staff (contacts, conversations, calendar, opportunities)."),
|
|
6727
6728
|
phone: import_zod22.z.string().optional().describe("Phone number (E.164 preferred)."),
|
|
6728
6729
|
password: import_zod22.z.string().min(8).optional().describe("Only if you must set one. Omit it (recommended) so GHL sends the invite instead."),
|
|
6729
|
-
permissions: import_zod22.z.record(import_zod22.z.boolean()).optional().describe("Override the default per-role permissions object entirely (GHL UserPermissions booleans)."),
|
|
6730
|
+
permissions: import_zod22.z.record(import_zod22.z.string(), import_zod22.z.boolean()).optional().describe("Override the default per-role permissions object entirely (GHL UserPermissions booleans)."),
|
|
6730
6731
|
companyId: import_zod22.z.string().optional().describe("Agency/company id. Resolved from the registered location by default; pass it only when that lookup fails."),
|
|
6731
6732
|
locationId: import_zod22.z.string().optional().describe("Sub-account to add the user to (defaults to the active location). The user is granted access to this ONE location.")
|
|
6732
6733
|
},
|
|
@@ -7432,10 +7433,11 @@ function registerEmailTools(server2, client) {
|
|
|
7432
7433
|
);
|
|
7433
7434
|
}
|
|
7434
7435
|
function registerEmailBuilderInternalTools(server2, builderClient, publicClient) {
|
|
7436
|
+
if (!builderClient) return;
|
|
7435
7437
|
const client = builderClient;
|
|
7436
|
-
if (!client) return;
|
|
7437
7438
|
async function builderRequest(method, path39, body) {
|
|
7438
7439
|
const headers = await client.buildHeaders();
|
|
7440
|
+
assertWriteAllowed(method, `${EMAIL_BUILDER_BASE}${path39}`, { defaultLocationId: client.locationId });
|
|
7439
7441
|
const response = await fetch(`${EMAIL_BUILDER_BASE}${path39}`, {
|
|
7440
7442
|
method,
|
|
7441
7443
|
headers,
|
|
@@ -7541,6 +7543,7 @@ var init_emails = __esm({
|
|
|
7541
7543
|
init_account_reads();
|
|
7542
7544
|
import_zod28 = require("zod");
|
|
7543
7545
|
init_tool_helpers();
|
|
7546
|
+
init_account_rules();
|
|
7544
7547
|
EMAIL_BUILDER_BASE = "https://backend.leadconnectorhq.com/emails/builder";
|
|
7545
7548
|
SNIPPETS_BASE = "https://services.leadconnectorhq.com/snippets";
|
|
7546
7549
|
TEMPLATE_TYPES = ["html", "folder", "import", "builder", "blank", "ai_template", "vibe-editor"];
|
|
@@ -7712,7 +7715,7 @@ function registerCustomObjectTools(server2, client) {
|
|
|
7712
7715
|
{
|
|
7713
7716
|
schemaKey: import_zod30.z.string().describe("The custom object schema key."),
|
|
7714
7717
|
locationId: import_zod30.z.string().optional().describe("GHL Location ID. Optional if GHL_LOCATION_ID is set in env."),
|
|
7715
|
-
properties: import_zod30.z.record(import_zod30.z.unknown()).describe("Key-value pairs of field values for the new record.")
|
|
7718
|
+
properties: import_zod30.z.record(import_zod30.z.string(), import_zod30.z.unknown()).describe("Key-value pairs of field values for the new record.")
|
|
7716
7719
|
},
|
|
7717
7720
|
async ({ schemaKey, locationId: locationId2, properties }) => {
|
|
7718
7721
|
const resolvedLocationId = client.resolveLocationId(locationId2);
|
|
@@ -7729,7 +7732,7 @@ function registerCustomObjectTools(server2, client) {
|
|
|
7729
7732
|
schemaKey: import_zod30.z.string().describe("The custom object schema key."),
|
|
7730
7733
|
recordId: import_zod30.z.string().describe("The record ID to update."),
|
|
7731
7734
|
locationId: import_zod30.z.string().optional().describe("GHL Location ID. Optional if GHL_LOCATION_ID is set in env."),
|
|
7732
|
-
properties: import_zod30.z.record(import_zod30.z.unknown()).describe("Key-value pairs of field values to update.")
|
|
7735
|
+
properties: import_zod30.z.record(import_zod30.z.string(), import_zod30.z.unknown()).describe("Key-value pairs of field values to update.")
|
|
7733
7736
|
},
|
|
7734
7737
|
async ({ schemaKey, recordId, locationId: locationId2, properties }) => {
|
|
7735
7738
|
const resolvedLocationId = client.resolveLocationId(locationId2);
|
|
@@ -7990,7 +7993,7 @@ function registerEstimateTools(server2, client) {
|
|
|
7990
7993
|
price: import_zod32.z.number().describe("Unit price, in the estimate currency."),
|
|
7991
7994
|
currency: import_zod32.z.string().optional().describe("Currency code (e.g. USD).")
|
|
7992
7995
|
})).optional().describe("Updated line items. Give quantity and price; they are translated to the qty/amount/type GHL requires."),
|
|
7993
|
-
discount: import_zod32.z.record(import_zod32.z.unknown()).optional().describe("Updated discount object.")
|
|
7996
|
+
discount: import_zod32.z.record(import_zod32.z.string(), import_zod32.z.unknown()).optional().describe("Updated discount object.")
|
|
7994
7997
|
},
|
|
7995
7998
|
async ({ estimateId, locationId: locationId2, name, title: title2, items, discount }) => {
|
|
7996
7999
|
const resolvedLocationId = client.resolveLocationId(locationId2);
|
|
@@ -8769,7 +8772,7 @@ function registerConversationAiTools(server2, client) {
|
|
|
8769
8772
|
agentId: import_zod35.z.string().describe("Agent id."),
|
|
8770
8773
|
type: import_zod35.z.enum(ACTION_TYPES),
|
|
8771
8774
|
name: import_zod35.z.string().min(1).describe("Name the operator sees."),
|
|
8772
|
-
details: import_zod35.z.record(import_zod35.z.unknown()).describe("Type-specific details. humanHandOver: enabled, triggerCondition, handoverType (contactRequest|lackOfInformation|failedToResolveIssue|custom), examples[], assignToUserId, finalMessage, reactivateEnabled(+sleepTime/Unit). appointmentBooking: calendarId, onlySendLink, triggerWorkflow(+workflowIds), sleepAfterBooking(+sleepTime/Unit), transferBot(+transferAgent), rescheduleEnabled, cancelEnabled. triggerWorkflow: workflowIds[], triggerCondition, triggerMessage. stopBot: stopBotDetectionType (Goodbye|Custom), stopBotTriggerCondition, stopBotExamples[] (2+), finalMessage, enabled, reactivateEnabled, tags[]. updateContactField: contactFieldId, description, contactUpdateExamples[]. transferBot: transferBotType, transferToBot, enabled. advancedFollowup: enabled, scenarioId, followupSequence[].")
|
|
8775
|
+
details: import_zod35.z.record(import_zod35.z.string(), import_zod35.z.unknown()).describe("Type-specific details. humanHandOver: enabled, triggerCondition, handoverType (contactRequest|lackOfInformation|failedToResolveIssue|custom), examples[], assignToUserId, finalMessage, reactivateEnabled(+sleepTime/Unit). appointmentBooking: calendarId, onlySendLink, triggerWorkflow(+workflowIds), sleepAfterBooking(+sleepTime/Unit), transferBot(+transferAgent), rescheduleEnabled, cancelEnabled. triggerWorkflow: workflowIds[], triggerCondition, triggerMessage. stopBot: stopBotDetectionType (Goodbye|Custom), stopBotTriggerCondition, stopBotExamples[] (2+), finalMessage, enabled, reactivateEnabled, tags[]. updateContactField: contactFieldId, description, contactUpdateExamples[]. transferBot: transferBotType, transferToBot, enabled. advancedFollowup: enabled, scenarioId, followupSequence[].")
|
|
8773
8776
|
},
|
|
8774
8777
|
async ({ agentId, type, name, details }) => {
|
|
8775
8778
|
const problem = actionBodyProblem(type, details);
|
|
@@ -8787,7 +8790,7 @@ function registerConversationAiTools(server2, client) {
|
|
|
8787
8790
|
actionId: import_zod35.z.string().describe("Action id."),
|
|
8788
8791
|
type: import_zod35.z.enum(ACTION_TYPES),
|
|
8789
8792
|
name: import_zod35.z.string().min(1),
|
|
8790
|
-
details: import_zod35.z.record(import_zod35.z.unknown()).describe("The complete details for this type.")
|
|
8793
|
+
details: import_zod35.z.record(import_zod35.z.string(), import_zod35.z.unknown()).describe("The complete details for this type.")
|
|
8791
8794
|
},
|
|
8792
8795
|
async ({ agentId, actionId, type, name, details }) => {
|
|
8793
8796
|
const problem = actionBodyProblem(type, details);
|
|
@@ -10024,7 +10027,7 @@ function registerVoiceAiTools(server2, shared) {
|
|
|
10024
10027
|
locationId: LOCATION,
|
|
10025
10028
|
actionType: import_zod37.z.enum(VOICE_ACTION_TYPES),
|
|
10026
10029
|
name: import_zod37.z.string().min(1).describe("Name the operator sees. Letters, numbers, spaces, _ and - only."),
|
|
10027
|
-
actionParameters: import_zod37.z.record(import_zod37.z.unknown()).describe(ACTION_PARAMS_DOC)
|
|
10030
|
+
actionParameters: import_zod37.z.record(import_zod37.z.string(), import_zod37.z.unknown()).describe(ACTION_PARAMS_DOC)
|
|
10028
10031
|
},
|
|
10029
10032
|
async ({ agentId, locationId: locationId2, actionType, name, actionParameters }) => {
|
|
10030
10033
|
const client = shared.pinned();
|
|
@@ -10060,7 +10063,7 @@ function registerVoiceAiTools(server2, shared) {
|
|
|
10060
10063
|
locationId: LOCATION,
|
|
10061
10064
|
actionType: import_zod37.z.enum(VOICE_ACTION_TYPES),
|
|
10062
10065
|
name: import_zod37.z.string().min(1),
|
|
10063
|
-
actionParameters: import_zod37.z.record(import_zod37.z.unknown()).describe("The complete parameters for this type.")
|
|
10066
|
+
actionParameters: import_zod37.z.record(import_zod37.z.string(), import_zod37.z.unknown()).describe("The complete parameters for this type.")
|
|
10064
10067
|
},
|
|
10065
10068
|
async ({ agentId, actionId, locationId: locationId2, actionType, name, actionParameters }) => {
|
|
10066
10069
|
const client = shared.pinned();
|
|
@@ -10288,12 +10291,15 @@ function readId(workflow) {
|
|
|
10288
10291
|
const id = workflow._id ?? record.id;
|
|
10289
10292
|
return typeof id === "string" ? id : "";
|
|
10290
10293
|
}
|
|
10294
|
+
function attributeBag(attributes) {
|
|
10295
|
+
return attributes ? Object.fromEntries(Object.entries(attributes)) : {};
|
|
10296
|
+
}
|
|
10291
10297
|
function summarizeWebhookWorkflow(workflow) {
|
|
10292
10298
|
const templates = workflow.workflowData?.templates ?? [];
|
|
10293
10299
|
const webhookActions = templates.filter((action) => action.type === "webhook");
|
|
10294
10300
|
if (!webhookActions.length) return null;
|
|
10295
10301
|
const actions = webhookActions.map((action) => {
|
|
10296
|
-
const attributes = action.attributes
|
|
10302
|
+
const attributes = attributeBag(action.attributes);
|
|
10297
10303
|
return {
|
|
10298
10304
|
actionId: action.id,
|
|
10299
10305
|
name: action.name,
|
|
@@ -10450,7 +10456,7 @@ function registerWebhookTools(server2, builderClient) {
|
|
|
10450
10456
|
}
|
|
10451
10457
|
const target = actionId ? webhookActions.find((a) => a.id === actionId) : webhookActions[0];
|
|
10452
10458
|
if (!target) throw new Error(`No webhook action with id ${actionId} on workflow ${webhookId}.`);
|
|
10453
|
-
const attributes = target.attributes
|
|
10459
|
+
const attributes = attributeBag(target.attributes);
|
|
10454
10460
|
const validated = buildWebhookAction({
|
|
10455
10461
|
id: target.id,
|
|
10456
10462
|
name: target.name,
|
|
@@ -10522,7 +10528,7 @@ var init_webhooks = __esm({
|
|
|
10522
10528
|
import_zod38.z.string().describe("A GHL trigger type, e.g. 'contact_created'. Fires with no filter."),
|
|
10523
10529
|
import_zod38.z.object({
|
|
10524
10530
|
type: import_zod38.z.string().describe("A GHL trigger type, e.g. 'contact_tag' or 'pipeline_stage_updated'."),
|
|
10525
|
-
conditions: import_zod38.z.array(import_zod38.z.record(import_zod38.z.unknown())).optional().describe(
|
|
10531
|
+
conditions: import_zod38.z.array(import_zod38.z.record(import_zod38.z.string(), import_zod38.z.unknown())).optional().describe(
|
|
10526
10532
|
"Filter conditions, exactly as update_workflow_actions takes them. Use get_trigger_registry to find the valid fields for a trigger type. Omit to fire on every event of that type."
|
|
10527
10533
|
)
|
|
10528
10534
|
})
|
|
@@ -10607,8 +10613,8 @@ function registerDocumentTools(server2, client) {
|
|
|
10607
10613
|
sentBy: import_zod39.z.string().optional().describe("GHL user ID of the sender (required by the API; falls back to GHL_USER_ID env)."),
|
|
10608
10614
|
documentName: import_zod39.z.string().optional().describe("Override the document name shown to recipients."),
|
|
10609
10615
|
medium: import_zod39.z.string().optional().describe("Delivery medium, e.g. 'email'. Defaults to email."),
|
|
10610
|
-
ccRecipients: import_zod39.z.array(import_zod39.z.record(import_zod39.z.unknown())).optional().describe("Optional CC recipients: [{id, email, contactName, firstName, lastName, imageUrl}]."),
|
|
10611
|
-
notificationSettings: import_zod39.z.record(import_zod39.z.unknown()).optional().describe("Optional sender/receiver notification settings: {sender:{fromName,fromEmail}, receive:{subject,templateId}}."),
|
|
10616
|
+
ccRecipients: import_zod39.z.array(import_zod39.z.record(import_zod39.z.string(), import_zod39.z.unknown())).optional().describe("Optional CC recipients: [{id, email, contactName, firstName, lastName, imageUrl}]."),
|
|
10617
|
+
notificationSettings: import_zod39.z.record(import_zod39.z.string(), import_zod39.z.unknown()).optional().describe("Optional sender/receiver notification settings: {sender:{fromName,fromEmail}, receive:{subject,templateId}}."),
|
|
10612
10618
|
locationId: import_zod39.z.string().optional().describe("GHL Location ID. Optional if GHL_LOCATION_ID is set in env.")
|
|
10613
10619
|
},
|
|
10614
10620
|
async ({ documentId, sentBy, documentName, medium, ccRecipients, notificationSettings, locationId: locationId2 }) => {
|
|
@@ -11055,7 +11061,7 @@ function registerWorkflowBuilderTools(server2, client) {
|
|
|
11055
11061
|
"invoice_paid",
|
|
11056
11062
|
"review_request_clicked"
|
|
11057
11063
|
]).describe("The goal-condition identifier. Extras shape per condition: email_event \u2192 {stepIds:[]}; link_click \u2192 {linkIds:[]}; appointment_status \u2192 {calendarId}; payment_received \u2192 {globalProductIds:[]}; form_submission \u2192 {formIds:[]}; document_status \u2192 {templateId}; invoice_paid \u2192 {invoiceStepId}; review_request_clicked \u2192 {reviewTypes:['sms'|'email'], reviewLinkId}; add_contact_tag/remove_contact_tag \u2192 {tags:[]} (uncaptured but inferred)."),
|
|
11058
|
-
extras: import_zod40.z.record(import_zod40.z.unknown()).optional().describe("Goal-condition-specific config. See goal_condition description for the expected shape per value. Pass {} or omit for goal_conditions without extras."),
|
|
11064
|
+
extras: import_zod40.z.record(import_zod40.z.string(), import_zod40.z.unknown()).optional().describe("Goal-condition-specific config. See goal_condition description for the expected shape per value. Pass {} or omit for goal_conditions without extras."),
|
|
11059
11065
|
action: import_zod40.z.enum(["exit", "continue", "wait"]).default("exit").describe("What happens when the goal fires (per GHL's GoalAction enum). 'exit' terminates the workflow path (verified). 'continue' lets it proceed to a downstream node. 'wait' pauses on the goal."),
|
|
11060
11066
|
name: import_zod40.z.string().default("Goal").describe("Display name in the GHL UI."),
|
|
11061
11067
|
op: import_zod40.z.enum(["or", "and"]).default("or").describe("Top-level boolean operator across segments. Default 'or'."),
|
|
@@ -11134,7 +11140,7 @@ var init_workflow_builder = __esm({
|
|
|
11134
11140
|
id: import_zod40.z.string().optional().describe("Action ID (auto-generated if omitted)."),
|
|
11135
11141
|
name: import_zod40.z.string().describe("Display name for this action step."),
|
|
11136
11142
|
type: import_zod40.z.string().describe("Action type."),
|
|
11137
|
-
attributes: import_zod40.z.record(import_zod40.z.unknown()).optional().describe("Action-specific configuration."),
|
|
11143
|
+
attributes: import_zod40.z.record(import_zod40.z.string(), import_zod40.z.unknown()).optional().describe("Action-specific configuration."),
|
|
11138
11144
|
next: import_zod40.z.union([import_zod40.z.string(), import_zod40.z.array(import_zod40.z.string())]).optional().describe("Next action id, or branch id array for condition nodes."),
|
|
11139
11145
|
parent: import_zod40.z.string().optional().describe("Branch parent node id."),
|
|
11140
11146
|
parentKey: import_zod40.z.string().optional().describe("Previous action id or branch parent id."),
|
|
@@ -11190,8 +11196,7 @@ var init_page_reads = __esm({
|
|
|
11190
11196
|
|
|
11191
11197
|
// src/tools/funnel-builder.ts
|
|
11192
11198
|
function registerFunnelBuilderTools(server2, builderClient) {
|
|
11193
|
-
|
|
11194
|
-
if (!client) {
|
|
11199
|
+
if (!builderClient) {
|
|
11195
11200
|
server2.tool(
|
|
11196
11201
|
"funnel_builder_status",
|
|
11197
11202
|
"Check whether the funnel / page builder is configured. It needs Firebase credentials, added via enable_workflow_builder.",
|
|
@@ -11219,6 +11224,7 @@ function registerFunnelBuilderTools(server2, builderClient) {
|
|
|
11219
11224
|
);
|
|
11220
11225
|
return;
|
|
11221
11226
|
}
|
|
11227
|
+
const client = builderClient;
|
|
11222
11228
|
async function internalGet(path39) {
|
|
11223
11229
|
return client.request("GET", path39);
|
|
11224
11230
|
}
|
|
@@ -11506,6 +11512,7 @@ ${text2}`);
|
|
|
11506
11512
|
headers.Origin = "https://page-builder.leadconnectorhq.com";
|
|
11507
11513
|
headers.Referer = "https://page-builder.leadconnectorhq.com/";
|
|
11508
11514
|
const url = `https://backend.leadconnectorhq.com/funnels/builder/autosave/${pageId}`;
|
|
11515
|
+
assertWriteAllowed("POST", url, { defaultLocationId: client.locationId });
|
|
11509
11516
|
const response = await fetch(url, { method: "POST", headers, body: JSON.stringify(body) });
|
|
11510
11517
|
if (!response.ok) {
|
|
11511
11518
|
throw new Error(`Funnel API Error ${response.status}: POST /builder/autosave/${pageId}
|
|
@@ -11524,7 +11531,7 @@ ${await response.text()}`);
|
|
|
11524
11531
|
"Update a funnel/website page's builder content \u2014 sections, elements, settings, pageStyles, trackingCode, popups, popupsList, fontsForPreview. Use get_page_content first to see the current structure, modify it, then pass the full envelope here. Writes via GHL's builder autosave and returns the new pageDataUrl. \u26A0\uFE0F GHL accepts a save onto a page that already has content but may never make it the visible version (live-tested 2026-07-28 and again 2026-08-31). The tool now VERIFIES this for you: every response carries `save_check`, and `save_check.visible: false` means the content was stored but visitors still see the previous page \u2014 rebuild on a fresh step (create_funnel_page) and save onto that. Never report a page as updated without reading save_check. Saves as a draft unless isPublished is true.",
|
|
11525
11532
|
{
|
|
11526
11533
|
pageId: import_zod41.z.string().describe("The page ID to update."),
|
|
11527
|
-
content: import_zod41.z.record(import_zod41.z.unknown()).describe("The full page content JSON \u2014 the 8-field envelope: sections, settings, general, pageStyles, trackingCode, fontsForPreview, popups, popupsList. Start from get_page_content and modify it."),
|
|
11534
|
+
content: import_zod41.z.record(import_zod41.z.string(), import_zod41.z.unknown()).describe("The full page content JSON \u2014 the 8-field envelope: sections, settings, general, pageStyles, trackingCode, fontsForPreview, popups, popupsList. Start from get_page_content and modify it."),
|
|
11528
11535
|
funnelId: import_zod41.z.string().optional().describe("Funnel ID that owns the page. Optional \u2014 auto-resolved from the page metadata if omitted."),
|
|
11529
11536
|
isPublished: import_zod41.z.boolean().optional().describe("Mark the page published after the save. Defaults to false (draft).")
|
|
11530
11537
|
},
|
|
@@ -11614,6 +11621,7 @@ var init_funnel_builder = __esm({
|
|
|
11614
11621
|
import_zod41 = require("zod");
|
|
11615
11622
|
init_tool_helpers();
|
|
11616
11623
|
init_page_reads();
|
|
11624
|
+
init_account_rules();
|
|
11617
11625
|
}
|
|
11618
11626
|
});
|
|
11619
11627
|
|
|
@@ -11814,6 +11822,9 @@ function requireCompany(api) {
|
|
|
11814
11822
|
}
|
|
11815
11823
|
async function call(api, path39, init) {
|
|
11816
11824
|
const headers = { ...await api.headers(), Origin: "https://app.gohighlevel.com", Referer: "https://app.gohighlevel.com/" };
|
|
11825
|
+
if (MUTATING_PATHS.some((mutating) => path39.startsWith(mutating))) {
|
|
11826
|
+
assertWriteAllowed("POST", SVC + path39, { defaultLocationId: api.locationId() });
|
|
11827
|
+
}
|
|
11817
11828
|
const doFetch = api.fetch ?? fetch;
|
|
11818
11829
|
const response = await doFetch(SVC + path39, { ...init, headers });
|
|
11819
11830
|
const text = await response.text();
|
|
@@ -11907,12 +11918,14 @@ function rankForBrand(rows, brand) {
|
|
|
11907
11918
|
return { ...t, why: parts.join("; ") };
|
|
11908
11919
|
});
|
|
11909
11920
|
}
|
|
11910
|
-
var SVC, GHL_ID;
|
|
11921
|
+
var SVC, MUTATING_PATHS, GHL_ID;
|
|
11911
11922
|
var init_ghl_templates = __esm({
|
|
11912
11923
|
"src/command-os/design/ghl-templates.ts"() {
|
|
11913
11924
|
"use strict";
|
|
11914
11925
|
init_color();
|
|
11926
|
+
init_account_rules();
|
|
11915
11927
|
SVC = "https://services.leadconnectorhq.com";
|
|
11928
|
+
MUTATING_PATHS = ["/templates/template/load"];
|
|
11916
11929
|
GHL_ID = /^[A-Za-z0-9]{15,40}$/;
|
|
11917
11930
|
}
|
|
11918
11931
|
});
|
|
@@ -16437,8 +16450,8 @@ function removalHoldBack(opts) {
|
|
|
16437
16450
|
return null;
|
|
16438
16451
|
}
|
|
16439
16452
|
function registerPageStudioTools(server2, builderClient) {
|
|
16453
|
+
if (!builderClient) return;
|
|
16440
16454
|
const client = builderClient;
|
|
16441
|
-
if (!client) return;
|
|
16442
16455
|
const templateApi = () => ({
|
|
16443
16456
|
headers: () => client.buildHeaders(),
|
|
16444
16457
|
companyId: () => client.getIntendedCompanyId() ?? client.getCurrentCompanyId(),
|
|
@@ -16487,7 +16500,9 @@ ${await response.text()}`);
|
|
|
16487
16500
|
const headers = await client.buildHeaders();
|
|
16488
16501
|
headers.Origin = "https://page-builder.leadconnectorhq.com";
|
|
16489
16502
|
headers.Referer = "https://page-builder.leadconnectorhq.com/";
|
|
16490
|
-
const
|
|
16503
|
+
const autosaveUrl = `https://backend.leadconnectorhq.com/funnels/builder/autosave/${pageId}`;
|
|
16504
|
+
assertWriteAllowed("POST", autosaveUrl, { defaultLocationId: client.locationId });
|
|
16505
|
+
const response = await fetch(autosaveUrl, {
|
|
16491
16506
|
method: "POST",
|
|
16492
16507
|
headers,
|
|
16493
16508
|
body: JSON.stringify(body)
|
|
@@ -16984,6 +16999,7 @@ var init_page_studio = __esm({
|
|
|
16984
16999
|
init_tool_helpers();
|
|
16985
17000
|
init_compose();
|
|
16986
17001
|
init_website();
|
|
17002
|
+
init_account_rules();
|
|
16987
17003
|
valueCardSchema = import_zod43.z.object({
|
|
16988
17004
|
title: import_zod43.z.string().describe("Card title (short, uppercase reads best)."),
|
|
16989
17005
|
bodyHtml: import_zod43.z.string().describe("Card body as HTML (<p>\u2026</p>). May use {{location.*}} merge fields."),
|
|
@@ -17074,6 +17090,7 @@ function cleanFormDataForSave(formData) {
|
|
|
17074
17090
|
async function formApiRequest(client, method, path39, body) {
|
|
17075
17091
|
const headers = await client.buildHeaders();
|
|
17076
17092
|
const url = `https://backend.leadconnectorhq.com/forms${path39}`;
|
|
17093
|
+
assertWriteAllowed(method, url, { defaultLocationId: client.locationId });
|
|
17077
17094
|
const options = { method, headers };
|
|
17078
17095
|
if (body && (method === "POST" || method === "PUT")) {
|
|
17079
17096
|
options.body = JSON.stringify(body);
|
|
@@ -17148,7 +17165,7 @@ function registerFormBuilderTools(server2, builderClient, publicClient) {
|
|
|
17148
17165
|
"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).",
|
|
17149
17166
|
{
|
|
17150
17167
|
formId: import_zod44.z.string().describe("The form ID to update."),
|
|
17151
|
-
formData: import_zod44.z.record(import_zod44.z.unknown()).describe("The updated formData object containing form fields, settings, autoResponder config, etc."),
|
|
17168
|
+
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."),
|
|
17152
17169
|
name: import_zod44.z.string().optional().describe("Updated form name. If omitted, the current name is preserved.")
|
|
17153
17170
|
},
|
|
17154
17171
|
async ({ formId, formData, name }) => {
|
|
@@ -17276,6 +17293,7 @@ var init_form_builder = __esm({
|
|
|
17276
17293
|
"use strict";
|
|
17277
17294
|
import_zod44 = require("zod");
|
|
17278
17295
|
init_tool_helpers();
|
|
17296
|
+
init_account_rules();
|
|
17279
17297
|
}
|
|
17280
17298
|
});
|
|
17281
17299
|
|
|
@@ -17407,7 +17425,7 @@ function registerFunnelQaTools(server2, client, builderClient) {
|
|
|
17407
17425
|
funnelUrl: import_zod45.z.string().describe("The live URL the test submission POSTs to (your external lead-bridge Worker URL, or any endpoint that creates the GHL contact). POSTed as JSON."),
|
|
17408
17426
|
sentinelEmail: import_zod45.z.string().optional().describe("Override the minted sentinel email. Default: a unique blueprint-qa.<id>@example.com (won't deliver; used only to find the contact)."),
|
|
17409
17427
|
testPhone: import_zod45.z.string().optional().describe("Include a phone on the submission (enables SMS-path checks). Must be a test-safe number you control \u2014 real SMS may send + bill."),
|
|
17410
|
-
submitBody: import_zod45.z.record(import_zod45.z.unknown()).optional().describe("Full JSON body to POST (advanced; for non-bridge endpoints). The sentinel email is injected if absent. Default body is the lead-bridge contract {first_name,last_name,email,phone,custom,_hp}."),
|
|
17428
|
+
submitBody: import_zod45.z.record(import_zod45.z.string(), import_zod45.z.unknown()).optional().describe("Full JSON body to POST (advanced; for non-bridge endpoints). The sentinel email is injected if absent. Default body is the lead-bridge contract {first_name,last_name,email,phone,custom,_hp}."),
|
|
17411
17429
|
expectCustom: import_zod45.z.array(customFieldInput).optional().describe("Custom fields the form sends \u2014 each is asserted to PERSIST with the sent value (field-value fidelity, Carlos #3)."),
|
|
17412
17430
|
expectStandard: import_zod45.z.array(import_zod45.z.string()).optional().describe("Standard contact keys to assert saved (e.g. ['phone','first_name']). email is always asserted."),
|
|
17413
17431
|
triggerTag: import_zod45.z.string().optional().describe("The tag the form/bridge should add (the speed-to-lead trigger). Asserted to land on the contact."),
|
|
@@ -17665,11 +17683,12 @@ var init_funnel_qa = __esm({
|
|
|
17665
17683
|
|
|
17666
17684
|
// src/tools/pipeline-builder.ts
|
|
17667
17685
|
function registerPipelineBuilderTools(server2, builderClient) {
|
|
17686
|
+
if (!builderClient) return;
|
|
17668
17687
|
const client = builderClient;
|
|
17669
|
-
if (!client) return;
|
|
17670
17688
|
async function pipelineRequest(method, path39, body) {
|
|
17671
17689
|
const headers = await client.buildHeaders();
|
|
17672
17690
|
const url = `https://backend.leadconnectorhq.com/opportunities/pipelines${path39}`;
|
|
17691
|
+
assertWriteAllowed(method, url, { defaultLocationId: client.locationId });
|
|
17673
17692
|
const options = { method, headers };
|
|
17674
17693
|
if (body && (method === "POST" || method === "PUT" || method === "PATCH")) {
|
|
17675
17694
|
options.body = JSON.stringify(body);
|
|
@@ -17852,6 +17871,7 @@ var init_pipeline_builder = __esm({
|
|
|
17852
17871
|
"src/tools/pipeline-builder.ts"() {
|
|
17853
17872
|
"use strict";
|
|
17854
17873
|
import_zod46 = require("zod");
|
|
17874
|
+
init_account_rules();
|
|
17855
17875
|
}
|
|
17856
17876
|
});
|
|
17857
17877
|
|
|
@@ -18580,19 +18600,20 @@ function deviceFingerprint(opts) {
|
|
|
18580
18600
|
function base64urlDecode(s) {
|
|
18581
18601
|
const b64 = s.replace(/-/g, "+").replace(/_/g, "/");
|
|
18582
18602
|
const padded = b64 + "=".repeat((4 - b64.length % 4) % 4);
|
|
18583
|
-
return new Uint8Array(Buffer.from(padded, "base64"));
|
|
18603
|
+
return new Uint8Array(new Uint8Array(Buffer.from(padded, "base64")).buffer);
|
|
18584
18604
|
}
|
|
18585
18605
|
async function getPublicKey() {
|
|
18586
18606
|
if (cachedPublicKey) return cachedPublicKey;
|
|
18587
18607
|
const raw = Buffer.from(ATTESTATION_PUBLIC_KEY_B64, "base64");
|
|
18588
|
-
|
|
18608
|
+
const key = await import_node_crypto3.webcrypto.subtle.importKey(
|
|
18589
18609
|
"raw",
|
|
18590
18610
|
raw,
|
|
18591
18611
|
{ name: "Ed25519" },
|
|
18592
18612
|
false,
|
|
18593
18613
|
["verify"]
|
|
18594
18614
|
);
|
|
18595
|
-
|
|
18615
|
+
cachedPublicKey = key;
|
|
18616
|
+
return key;
|
|
18596
18617
|
}
|
|
18597
18618
|
function parseAttestationPayload(token2) {
|
|
18598
18619
|
try {
|
|
@@ -20007,7 +20028,8 @@ function registerValidatorTools(server2, client, builderClient) {
|
|
|
20007
20028
|
const workflow = await builderClient.getWorkflow(workflowId);
|
|
20008
20029
|
if (!workflow) return errorResponse(new Error(`Workflow ${workflowId} not found`));
|
|
20009
20030
|
const refs = [];
|
|
20010
|
-
const
|
|
20031
|
+
const locationId2 = client.resolveLocationId();
|
|
20032
|
+
const socialPlatforms = await connectedSocialPlatforms(client, locationId2);
|
|
20011
20033
|
const shape = [
|
|
20012
20034
|
...checkActionShapes(workflow),
|
|
20013
20035
|
...checkSaveBlockers(workflow),
|
|
@@ -20020,7 +20042,7 @@ function registerValidatorTools(server2, client, builderClient) {
|
|
|
20020
20042
|
return jsonResponse({ workflowId, workflowName: workflow.name, ...summarizeFindings(shape), references_scanned: 0, actions_checked: actionsChecked, findings: shape });
|
|
20021
20043
|
const needWorkflows = refs.some((r) => r.kind === "workflow");
|
|
20022
20044
|
const catalog2 = needWorkflows ? await fullWorkflowCatalog(builderClient) : { ids: /* @__PURE__ */ new Set(), complete: true };
|
|
20023
|
-
const lookups = await fetchAndBuildLookups(client, builderClient,
|
|
20045
|
+
const lookups = await fetchAndBuildLookups(client, builderClient, locationId2, { ids: catalog2.ids, complete: catalog2.complete });
|
|
20024
20046
|
const findings = [...shape, ...checkRefs(refs, workflowId, lookups)];
|
|
20025
20047
|
const report2 = {
|
|
20026
20048
|
workflowId,
|
|
@@ -20042,7 +20064,7 @@ function registerValidatorTools(server2, client, builderClient) {
|
|
|
20042
20064
|
{},
|
|
20043
20065
|
async () => {
|
|
20044
20066
|
try {
|
|
20045
|
-
const locationId2 = client.
|
|
20067
|
+
const locationId2 = client.resolveLocationId();
|
|
20046
20068
|
const catalog2 = await fullWorkflowCatalog(builderClient);
|
|
20047
20069
|
if (catalog2.rows.length === 0)
|
|
20048
20070
|
return jsonResponse({
|
|
@@ -24836,7 +24858,7 @@ var init_plan = __esm({
|
|
|
24836
24858
|
workflows: import_zod51.z.array(workflowSchema).optional(),
|
|
24837
24859
|
handoffs: import_zod51.z.array(handoffSchema).optional(),
|
|
24838
24860
|
buildOrder: import_zod51.z.array(import_zod51.z.string()).optional(),
|
|
24839
|
-
idMap: import_zod51.z.record(import_zod51.z.string()).optional()
|
|
24861
|
+
idMap: import_zod51.z.record(import_zod51.z.string(), import_zod51.z.string()).optional()
|
|
24840
24862
|
}).strict();
|
|
24841
24863
|
PLAN_ERROR_CODES = {
|
|
24842
24864
|
/** A workflow named like /nurture/i whose waits add up to < 30 days. */
|
|
@@ -26374,15 +26396,29 @@ function newRunId(now = /* @__PURE__ */ new Date(), rand = defaultRand) {
|
|
|
26374
26396
|
function defaultRand() {
|
|
26375
26397
|
return Math.floor(Math.random() * 65536).toString(16).padStart(4, "0");
|
|
26376
26398
|
}
|
|
26399
|
+
function legacyFunnelParentage(plan) {
|
|
26400
|
+
const funnels = plan.funnels;
|
|
26401
|
+
if (!Array.isArray(funnels)) return [];
|
|
26402
|
+
const pairs = [];
|
|
26403
|
+
for (const funnel of funnels) {
|
|
26404
|
+
if (typeof funnel !== "object" || funnel === null) continue;
|
|
26405
|
+
const { ref, pages } = funnel;
|
|
26406
|
+
if (typeof ref !== "string" || !Array.isArray(pages)) continue;
|
|
26407
|
+
for (const page of pages) {
|
|
26408
|
+
if (typeof page !== "object" || page === null) continue;
|
|
26409
|
+
const pageRef = page.ref;
|
|
26410
|
+
if (typeof pageRef === "string") pairs.push([pageRef, ref]);
|
|
26411
|
+
}
|
|
26412
|
+
}
|
|
26413
|
+
return pairs;
|
|
26414
|
+
}
|
|
26377
26415
|
function parentRefIndex(plan) {
|
|
26378
26416
|
const parents = /* @__PURE__ */ new Map();
|
|
26379
26417
|
if (!plan) return parents;
|
|
26380
26418
|
for (const p of plan.pipelines ?? []) {
|
|
26381
26419
|
for (const st of p.stages ?? []) parents.set(st.ref, p.ref);
|
|
26382
26420
|
}
|
|
26383
|
-
for (const
|
|
26384
|
-
for (const pg of fn.pages ?? []) parents.set(pg.ref, fn.ref);
|
|
26385
|
-
}
|
|
26421
|
+
for (const [pageRef, funnelRef] of legacyFunnelParentage(plan)) parents.set(pageRef, funnelRef);
|
|
26386
26422
|
return parents;
|
|
26387
26423
|
}
|
|
26388
26424
|
function buildRunRecord(input) {
|
|
@@ -26955,8 +26991,13 @@ async function clientPreflight(registry2, locationId2) {
|
|
|
26955
26991
|
}
|
|
26956
26992
|
} catch {
|
|
26957
26993
|
}
|
|
26958
|
-
lines.push({
|
|
26959
|
-
|
|
26994
|
+
lines.push({
|
|
26995
|
+
key: "authentication",
|
|
26996
|
+
ok: authenticated,
|
|
26997
|
+
text: authenticated ? "Your Anthropic key authenticates, so powered runs will bill it." : key ? "The saved Anthropic key did not authenticate, so powered runs will fall back to your Claude sign-in on this computer. Replace it in Your agency, or remove it." : "No Anthropic key saved. Powered runs use your Claude sign-in on this computer and cost you nothing extra."
|
|
26998
|
+
});
|
|
26999
|
+
const blocking = lines.filter((l) => l.key !== "authentication");
|
|
27000
|
+
return { ok: blocking.every((l) => l.ok), lines };
|
|
26960
27001
|
}
|
|
26961
27002
|
async function agencyConnectionReady(registry2, companyId) {
|
|
26962
27003
|
const key = registry2.getAgencyKey();
|
|
@@ -28540,7 +28581,7 @@ var require_package = __commonJS({
|
|
|
28540
28581
|
"package.json"(exports2, module2) {
|
|
28541
28582
|
module2.exports = {
|
|
28542
28583
|
name: "@elitedcs/ghl-mcp",
|
|
28543
|
-
version: "3.83.
|
|
28584
|
+
version: "3.83.3",
|
|
28544
28585
|
mcpName: "io.github.drjerryrelth/ghl-command",
|
|
28545
28586
|
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.",
|
|
28546
28587
|
main: "dist/index.js",
|
|
@@ -28555,11 +28596,6 @@ var require_package = __commonJS({
|
|
|
28555
28596
|
"src/command-os/design/rules/anti-default.json",
|
|
28556
28597
|
"src/command-os/design/rules/web.json",
|
|
28557
28598
|
"src/command-os/design/rules/anti-default-contract.mjs",
|
|
28558
|
-
"src/command-os/design/rules/check-static.mjs",
|
|
28559
|
-
"src/command-os/design/rules/fixtures/claude-default.html",
|
|
28560
|
-
"src/command-os/design/rules/fixtures/house-clean.html",
|
|
28561
|
-
"src/command-os/design/rules/fixtures/generator-run-1.html",
|
|
28562
|
-
"src/command-os/design/rules/fixtures/external-css.html",
|
|
28563
28599
|
"dist/index.js",
|
|
28564
28600
|
"dist/capture-helper.js",
|
|
28565
28601
|
"templates/action-schemas.json",
|
|
@@ -28577,6 +28613,7 @@ var require_package = __commonJS({
|
|
|
28577
28613
|
],
|
|
28578
28614
|
scripts: {
|
|
28579
28615
|
build: "node src/command-os/assessment/build-app.mjs && esbuild src/index.ts --bundle --platform=node --target=node20 --format=cjs --outfile=dist/index.js --packages=external && esbuild src/capture-helper.ts --bundle --platform=node --target=node20 --format=cjs --outfile=dist/capture-helper.js --packages=external",
|
|
28616
|
+
typecheck: "tsc --noEmit",
|
|
28580
28617
|
setup: "node setup-wizard.mjs",
|
|
28581
28618
|
catalogue: "node scripts/export-command-os-catalogue.mjs",
|
|
28582
28619
|
"manifest:write": "node scripts/write-package-manifest.mjs",
|
|
@@ -29001,7 +29038,8 @@ function writeClientEngagement(locationId2, patch, file2 = clientEngagementsPath
|
|
|
29001
29038
|
throw new Error("Something else saved this record while you were editing. Nothing was changed \u2014 reload and enter it again.");
|
|
29002
29039
|
}
|
|
29003
29040
|
function floorFor(e, profile) {
|
|
29004
|
-
const
|
|
29041
|
+
const wanted = e.offer?.trim().toLowerCase();
|
|
29042
|
+
const offer = wanted ? profile.offers?.find((o) => o.name.trim().toLowerCase() === wanted) : void 0;
|
|
29005
29043
|
if (offer?.priceFloor !== void 0) return offer.priceFloor;
|
|
29006
29044
|
if (e.cadence === "monthly") return profile.pricing?.minMonthly;
|
|
29007
29045
|
if (e.cadence === "one-time") return profile.pricing?.minSetup;
|
|
@@ -30024,13 +30062,14 @@ function pickLatest(notes) {
|
|
|
30024
30062
|
}
|
|
30025
30063
|
return null;
|
|
30026
30064
|
}
|
|
30027
|
-
var PIPELINE_NAME, NOTE_TITLE, MACHINE_MARKER, MARKER_LINE, NOTE_LIMIT;
|
|
30065
|
+
var PIPELINE_NAME, NOTE_TITLE, MACHINE_MARKER, STAGES, MARKER_LINE, NOTE_LIMIT;
|
|
30028
30066
|
var init_store2 = __esm({
|
|
30029
30067
|
"src/command-os/assessment/store.ts"() {
|
|
30030
30068
|
"use strict";
|
|
30031
30069
|
PIPELINE_NAME = "Assessments";
|
|
30032
30070
|
NOTE_TITLE = "GHL COMMAND ASSESSMENT";
|
|
30033
30071
|
MACHINE_MARKER = "--- machine block, do not edit below this line ---";
|
|
30072
|
+
STAGES = ["Gathering", "Findings", "Proposal sent", "Won", "Lost"];
|
|
30034
30073
|
MARKER_LINE = /^--- machine block, do not edit below this line ---(?: \((\d+)\/(\d+)(?: s=([a-z0-9]{6}))?\))?[ \t]*$/gm;
|
|
30035
30074
|
NOTE_LIMIT = 2e4;
|
|
30036
30075
|
}
|
|
@@ -30044,7 +30083,8 @@ async function loadAssessmentRecord(reader, contactId) {
|
|
|
30044
30083
|
}
|
|
30045
30084
|
async function findAssessmentPipeline(reader, locationId2) {
|
|
30046
30085
|
const raw = await reader.get("/opportunities/pipelines", { params: { locationId: locationId2 } });
|
|
30047
|
-
|
|
30086
|
+
const pipelines = Array.isArray(raw?.pipelines) ? raw.pipelines : [];
|
|
30087
|
+
return pipelines.find((p) => String(p.name).trim().toLowerCase() === PIPELINE_NAME.toLowerCase());
|
|
30048
30088
|
}
|
|
30049
30089
|
async function listAssessmentRecords(reader, locationId2) {
|
|
30050
30090
|
const pipeline = await findAssessmentPipeline(reader, locationId2);
|
|
@@ -33394,7 +33434,7 @@ function registerAssessmentTools(server2, client) {
|
|
|
33394
33434
|
quote: import_zod52.z.string().describe("The owner's own words, VERBATIM from the transcript. Do not tidy it up \u2014 if it is not in the transcript the answer is thrown out."),
|
|
33395
33435
|
reasoning: import_zod52.z.string().optional().describe("How you got from the quote to the value, when they are not the same.")
|
|
33396
33436
|
})).describe("One entry per question you could answer. Leave out anything the conversation did not cover."),
|
|
33397
|
-
existing: import_zod52.z.record(import_zod52.z.union([import_zod52.z.string(), import_zod52.z.number()])).optional().describe("Answers already captured, so this adds to them rather than replacing them.")
|
|
33437
|
+
existing: import_zod52.z.record(import_zod52.z.string(), import_zod52.z.union([import_zod52.z.string(), import_zod52.z.number()])).optional().describe("Answers already captured, so this adds to them rather than replacing them.")
|
|
33398
33438
|
},
|
|
33399
33439
|
async ({ transcript, proposed, existing }) => {
|
|
33400
33440
|
const gated = gateExtraction(transcript, proposed, QUESTION_UNITS);
|
|
@@ -33517,9 +33557,9 @@ var init_assessment = __esm({
|
|
|
33517
33557
|
StateSchema = import_zod52.z.object({
|
|
33518
33558
|
biz: import_zod52.z.string().min(1).describe("The prospect's business name."),
|
|
33519
33559
|
trade: import_zod52.z.string().optional().describe("Their trade. Decides which benchmarks may be applied."),
|
|
33520
|
-
ans: import_zod52.z.record(import_zod52.z.union([import_zod52.z.string(), import_zod52.z.number()])).describe("questionId -> answer, exactly as typed."),
|
|
33521
|
-
skip: import_zod52.z.record(import_zod52.z.boolean()).optional().describe("questionId -> true where they did not know."),
|
|
33522
|
-
asm: import_zod52.z.record(import_zod52.z.number()).optional().describe("assumption id -> rate as a decimal."),
|
|
33560
|
+
ans: import_zod52.z.record(import_zod52.z.string(), import_zod52.z.union([import_zod52.z.string(), import_zod52.z.number()])).describe("questionId -> answer, exactly as typed."),
|
|
33561
|
+
skip: import_zod52.z.record(import_zod52.z.string(), import_zod52.z.boolean()).optional().describe("questionId -> true where they did not know."),
|
|
33562
|
+
asm: import_zod52.z.record(import_zod52.z.string(), import_zod52.z.number()).optional().describe("assumption id -> rate as a decimal."),
|
|
33523
33563
|
client: import_zod52.z.object({ name: import_zod52.z.string().max(120), role: import_zod52.z.string().max(80), email: import_zod52.z.string().max(200), phone: import_zod52.z.string().max(40), website: import_zod52.z.string().max(300), city: import_zod52.z.string().max(120) }).partial().optional().describe("The person and place this is for, as typed on The client step. Rides with the answers so a load brings it back.")
|
|
33524
33564
|
});
|
|
33525
33565
|
}
|
|
@@ -34096,7 +34136,7 @@ function startRun(locationId2, stage, onDone, meta = {}) {
|
|
|
34096
34136
|
const obligations = (context.manualObligations ?? []).filter((o) => !deps.some((d) => d.ref === o.ref) && !(outcome.manualChecks ?? []).some((c) => c.ref === o.ref && c.type === o.type && c.ok && c.evidence.trim()));
|
|
34097
34137
|
for (const h of plan?.plan?.handoffs ?? []) if (!obligations.some((o) => o.ref === h.ref) && !(outcome.manualChecks ?? []).some((c) => c.ref === h.ref && c.ok && c.evidence.trim())) obligations.push({ ref: h.ref, type: "handoff", name: h.title || h.name || h.ref, reason: h.instruction || "Finish this manual action." });
|
|
34098
34138
|
outcome.ok = verifyBuildPassed(outcome, readback, plan?.plan);
|
|
34099
|
-
verifyRaw = { summary: outcome.summary, issues: outcome.issues, verification: { deps, checks, readback, obligations, buildOk: outcome.ok, liveProofApplicable: Boolean(plan?.plan
|
|
34139
|
+
verifyRaw = { summary: outcome.summary, issues: outcome.issues, verification: { deps, checks, readback, obligations, buildOk: outcome.ok, liveProofApplicable: Boolean(plan?.plan && typeof plan.plan === "object" && "funnels" in plan.plan && Array.isArray(plan.plan.funnels) && plan.plan.funnels.length), liveProof: outcome.liveProof ?? { ok: false, evidence: outcome.error || "No test-lead result was returned." } } };
|
|
34100
34140
|
if (!plan?.plan) {
|
|
34101
34141
|
outcome.ok = false;
|
|
34102
34142
|
verifyRaw.issues = [...verifyRaw.issues ?? [], "Run Build before verifying."];
|
|
@@ -35190,7 +35230,7 @@ function card(c,state,run){const st=(state.clients[c.locationId]||{stages:STAGES
|
|
|
35190
35230
|
el.appendChild(box)}
|
|
35191
35231
|
// One short line here; the button's confirmation carries the full wording. At 135
|
|
35192
35232
|
// characters this sentence made every stage box 677 px tall (auditor, 2026-09-07).
|
|
35193
|
-
if(i===4){const disclosure=document.createElement("small");disclosure.textContent="
|
|
35233
|
+
if(i===4){const disclosure=document.createElement("small");disclosure.textContent="Reads every planned item back. May submit and delete one test lead.";el.appendChild(disclosure)}
|
|
35194
35234
|
if(gate)el.title=gate;
|
|
35195
35235
|
if(stale&&!live){const reason=document.createElement("small");reason.textContent=stale.reason;el.appendChild(reason)}
|
|
35196
35236
|
const oc=(state.outcomes||{})[c.locationId+":"+i];
|
|
@@ -36987,8 +37027,9 @@ async function runDashboard(argv) {
|
|
|
36987
37027
|
state = setAccountType(state, outcome.locationId, "client");
|
|
36988
37028
|
if (args.industry) state = setAccountIndustry(state, outcome.locationId, args.industry);
|
|
36989
37029
|
if (!outcome.registered) {
|
|
36990
|
-
|
|
36991
|
-
if (outcome.saveFailed)
|
|
37030
|
+
const pending = setPending(state, outcome.locationId, args.name, args.industry || void 0);
|
|
37031
|
+
if (outcome.saveFailed) pending.pending[outcome.locationId].saveFailed = true;
|
|
37032
|
+
state = pending;
|
|
36992
37033
|
}
|
|
36993
37034
|
writeCockpitState(state);
|
|
36994
37035
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
@@ -37996,7 +38037,7 @@ function registerBulkOperationTools(server2, client) {
|
|
|
37996
38037
|
"Update the same field(s) on multiple contacts at once. Rate-limited. Example: set a custom field value, change source, update address for a batch of contacts.",
|
|
37997
38038
|
{
|
|
37998
38039
|
contactIds: import_zod54.z.array(import_zod54.z.string()).min(1, "At least one contact ID required.").describe("Array of contact IDs to update."),
|
|
37999
|
-
fields: import_zod54.z.record(import_zod54.z.unknown()).describe("Fields to set on each contact (e.g. {customField: {id: 'xxx', value: 'yyy'}}, {source: 'Import'}).")
|
|
38040
|
+
fields: import_zod54.z.record(import_zod54.z.string(), import_zod54.z.unknown()).describe("Fields to set on each contact (e.g. {customField: {id: 'xxx', value: 'yyy'}}, {source: 'Import'}).")
|
|
38000
38041
|
},
|
|
38001
38042
|
async ({ contactIds, fields }) => {
|
|
38002
38043
|
if (Object.keys(fields).length === 0) {
|
|
@@ -39054,10 +39095,12 @@ function registerWorkflowClonerTools(server2, builderClient) {
|
|
|
39054
39095
|
workflowId: void 0
|
|
39055
39096
|
// Will be set by the new workflow
|
|
39056
39097
|
};
|
|
39057
|
-
|
|
39098
|
+
const clonedData = cloned.data;
|
|
39099
|
+
if (clonedData && typeof clonedData === "object" && "targetActionId" in clonedData) {
|
|
39100
|
+
const targetActionId = clonedData.targetActionId;
|
|
39058
39101
|
cloned.data = {
|
|
39059
|
-
...
|
|
39060
|
-
targetActionId: remap(
|
|
39102
|
+
...clonedData,
|
|
39103
|
+
targetActionId: typeof targetActionId === "string" ? remap(targetActionId) : targetActionId
|
|
39061
39104
|
};
|
|
39062
39105
|
}
|
|
39063
39106
|
return cloned;
|
|
@@ -39170,11 +39213,12 @@ function buildContactSmartListBody(args) {
|
|
|
39170
39213
|
};
|
|
39171
39214
|
}
|
|
39172
39215
|
function registerSmartListTools(server2, builderClient) {
|
|
39216
|
+
if (!builderClient) return;
|
|
39173
39217
|
const client = builderClient;
|
|
39174
|
-
if (!client) return;
|
|
39175
39218
|
async function smartListRequest(method, path39, body) {
|
|
39176
39219
|
const headers = await client.buildHeaders();
|
|
39177
39220
|
const url = `${SMARTLIST_BASE}${path39}`;
|
|
39221
|
+
assertWriteAllowed(method, url, { defaultLocationId: client.locationId });
|
|
39178
39222
|
const options = { method, headers };
|
|
39179
39223
|
if (body && (method === "POST" || method === "PUT")) {
|
|
39180
39224
|
options.body = JSON.stringify(body);
|
|
@@ -39232,7 +39276,7 @@ ${await res.text()}`);
|
|
|
39232
39276
|
{
|
|
39233
39277
|
name: import_zod57.z.string().describe("Display name for the smart list."),
|
|
39234
39278
|
tagFilters: import_zod57.z.array(import_zod57.z.array(import_zod57.z.string())).optional().describe('Tag conditions. Each inner array is one filter requiring ALL those tags, e.g. [["escalated"]] for "has the escalated tag".'),
|
|
39235
|
-
rawFilters: import_zod57.z.array(import_zod57.z.record(import_zod57.z.unknown())).optional().describe("Advanced: raw filter leaves {field, operator, value, options}. Combined with tagFilters into one AND group."),
|
|
39279
|
+
rawFilters: import_zod57.z.array(import_zod57.z.record(import_zod57.z.string(), import_zod57.z.unknown())).optional().describe("Advanced: raw filter leaves {field, operator, value, options}. Combined with tagFilters into one AND group."),
|
|
39236
39280
|
locationId: import_zod57.z.string().optional().describe("Location ID. Falls back to the active builder client's location.")
|
|
39237
39281
|
},
|
|
39238
39282
|
async ({ name, tagFilters, rawFilters, locationId: locationId2 }) => {
|
|
@@ -39244,6 +39288,7 @@ ${await res.text()}`);
|
|
|
39244
39288
|
];
|
|
39245
39289
|
const body = buildContactSmartListBody({ listName: name, locationId: loc, filters });
|
|
39246
39290
|
const headers = await client.buildHeaders();
|
|
39291
|
+
assertWriteAllowed("POST", CONTACT_SMARTLIST_BASE, { defaultLocationId: client.locationId });
|
|
39247
39292
|
const res = await fetch(CONTACT_SMARTLIST_BASE, { method: "POST", headers, body: JSON.stringify(body) });
|
|
39248
39293
|
if (!res.ok) throw new Error(`Smart Lists API Error ${res.status}: POST /contacts/smartlist
|
|
39249
39294
|
${await res.text()}`);
|
|
@@ -39281,6 +39326,7 @@ ${await res.text()}`);
|
|
|
39281
39326
|
async ({ listId }) => {
|
|
39282
39327
|
try {
|
|
39283
39328
|
const headers = await client.buildHeaders();
|
|
39329
|
+
assertWriteAllowed("POST", CONTACT_SMARTLIST_DELETE, { defaultLocationId: client.locationId });
|
|
39284
39330
|
const res = await fetch(CONTACT_SMARTLIST_DELETE, {
|
|
39285
39331
|
method: "POST",
|
|
39286
39332
|
headers,
|
|
@@ -39317,8 +39363,8 @@ ${await res.text()}`);
|
|
|
39317
39363
|
{
|
|
39318
39364
|
name: import_zod57.z.string().describe("Display name for the smart list."),
|
|
39319
39365
|
objectKey: import_zod57.z.enum(OBJECT_KEYS).describe("Object type the list segments over. 'contacts' or 'opportunity'."),
|
|
39320
|
-
filters: import_zod57.z.array(import_zod57.z.record(import_zod57.z.unknown())).optional().describe("Array of filter objects. Each object has fields like {field, operator, value} \u2014 exact shape varies by filter type. See get_smart_list on an existing list to learn the format."),
|
|
39321
|
-
columns: import_zod57.z.array(import_zod57.z.record(import_zod57.z.unknown())).optional().describe("Array of column definitions for the smart list view in GHL UI. Each defines which contact/opportunity field shows as a column. Defaults to GHL's standard columns if omitted."),
|
|
39366
|
+
filters: import_zod57.z.array(import_zod57.z.record(import_zod57.z.string(), import_zod57.z.unknown())).optional().describe("Array of filter objects. Each object has fields like {field, operator, value} \u2014 exact shape varies by filter type. See get_smart_list on an existing list to learn the format."),
|
|
39367
|
+
columns: import_zod57.z.array(import_zod57.z.record(import_zod57.z.string(), import_zod57.z.unknown())).optional().describe("Array of column definitions for the smart list view in GHL UI. Each defines which contact/opportunity field shows as a column. Defaults to GHL's standard columns if omitted."),
|
|
39322
39368
|
pipelineIds: import_zod57.z.array(import_zod57.z.string()).optional().describe("(opportunity objectKey only) Pipeline IDs to restrict this smart list to. Empty array = all pipelines."),
|
|
39323
39369
|
defaultInPipelines: import_zod57.z.array(import_zod57.z.string()).optional().describe("(opportunity objectKey only) Pipeline IDs where this list is the default view."),
|
|
39324
39370
|
locationId: import_zod57.z.string().optional().describe("Location ID. Falls back to the active builder client's location.")
|
|
@@ -39344,8 +39390,8 @@ ${await res.text()}`);
|
|
|
39344
39390
|
{
|
|
39345
39391
|
listId: import_zod57.z.string().describe("The smart list ID to update."),
|
|
39346
39392
|
name: import_zod57.z.string().optional().describe("New display name."),
|
|
39347
|
-
filters: import_zod57.z.array(import_zod57.z.record(import_zod57.z.unknown())).optional().describe("Replace the filter array entirely. To add a filter, fetch the current list, append, and pass the new array."),
|
|
39348
|
-
columns: import_zod57.z.array(import_zod57.z.record(import_zod57.z.unknown())).optional().describe("Replace the column array entirely."),
|
|
39393
|
+
filters: import_zod57.z.array(import_zod57.z.record(import_zod57.z.string(), import_zod57.z.unknown())).optional().describe("Replace the filter array entirely. To add a filter, fetch the current list, append, and pass the new array."),
|
|
39394
|
+
columns: import_zod57.z.array(import_zod57.z.record(import_zod57.z.string(), import_zod57.z.unknown())).optional().describe("Replace the column array entirely."),
|
|
39349
39395
|
pipelineIds: import_zod57.z.array(import_zod57.z.string()).optional().describe("(opportunity only) Update the pipeline scope."),
|
|
39350
39396
|
defaultInPipelines: import_zod57.z.array(import_zod57.z.string()).optional().describe("(opportunity only) Update the default-in-pipelines list."),
|
|
39351
39397
|
locationId: import_zod57.z.string().optional().describe("Location ID. Falls back to the active builder client's location.")
|
|
@@ -39394,6 +39440,7 @@ var init_smart_lists = __esm({
|
|
|
39394
39440
|
"use strict";
|
|
39395
39441
|
import_zod57 = require("zod");
|
|
39396
39442
|
init_tool_helpers();
|
|
39443
|
+
init_account_rules();
|
|
39397
39444
|
SMARTLIST_BASE = "https://backend.leadconnectorhq.com/lists/dynamic";
|
|
39398
39445
|
OBJECT_KEYS = ["contacts", "opportunity"];
|
|
39399
39446
|
CONTACT_SMARTLIST_BASE = "https://services.leadconnectorhq.com/contacts/smartlist";
|
|
@@ -39412,9 +39459,10 @@ var init_smart_lists = __esm({
|
|
|
39412
39459
|
|
|
39413
39460
|
// src/tools/reputation.ts
|
|
39414
39461
|
function registerReputationTools(server2, builderClient) {
|
|
39462
|
+
if (!builderClient) return;
|
|
39415
39463
|
const client = builderClient;
|
|
39416
|
-
if (!client) return;
|
|
39417
39464
|
async function reputationRequest(method, path39) {
|
|
39465
|
+
assertWriteAllowed(method, `${REPUTATION_BASE}${path39}`, { defaultLocationId: client.locationId });
|
|
39418
39466
|
const headers = await client.buildHeaders();
|
|
39419
39467
|
const response = await fetch(`${REPUTATION_BASE}${path39}`, { method, headers });
|
|
39420
39468
|
if (!response.ok) {
|
|
@@ -39493,6 +39541,7 @@ var init_reputation = __esm({
|
|
|
39493
39541
|
"use strict";
|
|
39494
39542
|
import_zod58 = require("zod");
|
|
39495
39543
|
init_tool_helpers();
|
|
39544
|
+
init_account_rules();
|
|
39496
39545
|
REPUTATION_BASE = "https://backend.leadconnectorhq.com/reputation";
|
|
39497
39546
|
}
|
|
39498
39547
|
});
|
|
@@ -39527,6 +39576,7 @@ function registerEmailCampaignTools(server2, builderClient) {
|
|
|
39527
39576
|
if (enableResendToUnopened !== void 0) body.enableResendToUnopened = enableResendToUnopened;
|
|
39528
39577
|
if (hasUtmTracking !== void 0) body.hasUtmTracking = hasUtmTracking;
|
|
39529
39578
|
const headers = await client.buildHeaders();
|
|
39579
|
+
assertWriteAllowed("POST", `${SVC_BASE}/emails/schedule`, { defaultLocationId: client.locationId });
|
|
39530
39580
|
const response = await fetch(`${SVC_BASE}/emails/schedule`, {
|
|
39531
39581
|
method: "POST",
|
|
39532
39582
|
headers,
|
|
@@ -39552,15 +39602,17 @@ var init_email_campaigns = __esm({
|
|
|
39552
39602
|
"use strict";
|
|
39553
39603
|
import_zod59 = require("zod");
|
|
39554
39604
|
init_tool_helpers();
|
|
39605
|
+
init_account_rules();
|
|
39555
39606
|
SVC_BASE = "https://services.leadconnectorhq.com";
|
|
39556
39607
|
}
|
|
39557
39608
|
});
|
|
39558
39609
|
|
|
39559
39610
|
// src/tools/memberships.ts
|
|
39560
39611
|
function registerMembershipTools(server2, builderClient) {
|
|
39612
|
+
if (!builderClient) return;
|
|
39561
39613
|
const client = builderClient;
|
|
39562
|
-
if (!client) return;
|
|
39563
39614
|
async function membershipRequest(path39, method = "GET", body) {
|
|
39615
|
+
assertWriteAllowed(method, `${MEMBERSHIP_BASE2}${path39}`, { defaultLocationId: client.locationId });
|
|
39564
39616
|
const headers = await client.buildHeaders();
|
|
39565
39617
|
const response = await fetch(`${MEMBERSHIP_BASE2}${path39}`, {
|
|
39566
39618
|
method,
|
|
@@ -39748,6 +39800,7 @@ var init_memberships = __esm({
|
|
|
39748
39800
|
"use strict";
|
|
39749
39801
|
import_zod60 = require("zod");
|
|
39750
39802
|
init_tool_helpers();
|
|
39803
|
+
init_account_rules();
|
|
39751
39804
|
MEMBERSHIP_BASE2 = "https://backend.leadconnectorhq.com/membership";
|
|
39752
39805
|
}
|
|
39753
39806
|
});
|
|
@@ -39875,7 +39928,7 @@ function registerTemplateDeployerTools(server2, client) {
|
|
|
39875
39928
|
"Deploy a template to set up a GHL sub-account. Creates tags, custom fields, pipelines with stages, calendars, workflows, and forms based on the template and the user's questionnaire answers. This is the main setup automation tool.",
|
|
39876
39929
|
{
|
|
39877
39930
|
templateFile: import_zod61.z.string().describe("Path to the template JSON file."),
|
|
39878
|
-
answers: import_zod61.z.record(import_zod61.z.unknown()).describe("Questionnaire answers keyed by question ID (e.g. {business_name: 'My Clinic', business_phone: '+15551234567', ...})."),
|
|
39931
|
+
answers: import_zod61.z.record(import_zod61.z.string(), import_zod61.z.unknown()).describe("Questionnaire answers keyed by question ID (e.g. {business_name: 'My Clinic', business_phone: '+15551234567', ...})."),
|
|
39879
39932
|
locationId: import_zod61.z.string().optional().describe("Location ID to deploy to. Uses default if not specified."),
|
|
39880
39933
|
dryRun: import_zod61.z.boolean().optional().describe("If true, shows what would be created without actually creating anything. Defaults to false."),
|
|
39881
39934
|
overwriteBusinessInfo: import_zod61.z.boolean().optional().describe(
|
|
@@ -40096,6 +40149,7 @@ Pipelines: ${template.pipelines.length} to create`);
|
|
|
40096
40149
|
} else {
|
|
40097
40150
|
try {
|
|
40098
40151
|
const headers = await builderClient.buildHeaders();
|
|
40152
|
+
assertWriteAllowed("POST", "https://backend.leadconnectorhq.com/opportunities/pipelines", { defaultLocationId: locId });
|
|
40099
40153
|
const res = await fetch("https://backend.leadconnectorhq.com/opportunities/pipelines", {
|
|
40100
40154
|
method: "POST",
|
|
40101
40155
|
headers,
|
|
@@ -40270,6 +40324,7 @@ var init_template_deployer = __esm({
|
|
|
40270
40324
|
init_tool_helpers();
|
|
40271
40325
|
fs28 = __toESM(require("fs"));
|
|
40272
40326
|
path27 = __toESM(require("path"));
|
|
40327
|
+
init_account_rules();
|
|
40273
40328
|
TemplateSchema = import_zod61.z.object({
|
|
40274
40329
|
templateName: import_zod61.z.string(),
|
|
40275
40330
|
templateVersion: import_zod61.z.string().optional(),
|
|
@@ -40281,7 +40336,7 @@ var init_template_deployer = __esm({
|
|
|
40281
40336
|
required: import_zod61.z.boolean().optional(),
|
|
40282
40337
|
placeholder: import_zod61.z.string().optional()
|
|
40283
40338
|
})).optional().default([]),
|
|
40284
|
-
location: import_zod61.z.record(import_zod61.z.unknown()).optional(),
|
|
40339
|
+
location: import_zod61.z.record(import_zod61.z.string(), import_zod61.z.unknown()).optional(),
|
|
40285
40340
|
tags: import_zod61.z.array(import_zod61.z.string()).optional(),
|
|
40286
40341
|
customFields: import_zod61.z.array(import_zod61.z.object({
|
|
40287
40342
|
name: import_zod61.z.string(),
|
|
@@ -40294,7 +40349,7 @@ var init_template_deployer = __esm({
|
|
|
40294
40349
|
workflows: import_zod61.z.array(import_zod61.z.object({
|
|
40295
40350
|
name: import_zod61.z.string(),
|
|
40296
40351
|
condition: import_zod61.z.string().optional(),
|
|
40297
|
-
actions: import_zod61.z.array(import_zod61.z.record(import_zod61.z.unknown())).optional().default([])
|
|
40352
|
+
actions: import_zod61.z.array(import_zod61.z.record(import_zod61.z.string(), import_zod61.z.unknown())).optional().default([])
|
|
40298
40353
|
})).optional(),
|
|
40299
40354
|
calendars: import_zod61.z.array(import_zod61.z.object({
|
|
40300
40355
|
name: import_zod61.z.string(),
|
|
@@ -40334,13 +40389,17 @@ var init_version_check = __esm({
|
|
|
40334
40389
|
});
|
|
40335
40390
|
|
|
40336
40391
|
// src/tools/diagnostics.ts
|
|
40392
|
+
function anthropicKeyCheckStatus(verdict) {
|
|
40393
|
+
const severity = ANTHROPIC_KEY_STATUS[verdict];
|
|
40394
|
+
return severity === "ok" ? "pass" : severity;
|
|
40395
|
+
}
|
|
40337
40396
|
async function collectInstallChecks(installedVersion, client, builderClient, registry2, getGatingReport, plan = "mcp", probeAnthropic = probeAnthropicKey) {
|
|
40338
40397
|
const checks = [];
|
|
40339
40398
|
const anthropicPromise = plan === "command-os" ? (async () => {
|
|
40340
40399
|
const r = await probeAnthropic({ apiKey: process.env.ANTHROPIC_API_KEY });
|
|
40341
40400
|
return {
|
|
40342
40401
|
name: "Anthropic key (Command OS demo builder)",
|
|
40343
|
-
status:
|
|
40402
|
+
status: anthropicKeyCheckStatus(r.verdict),
|
|
40344
40403
|
detail: r.raw ? `${r.detail} Anthropic said: "${r.raw}"` : r.detail
|
|
40345
40404
|
};
|
|
40346
40405
|
})() : null;
|
|
@@ -46256,7 +46315,7 @@ function registerAgencyBuildTools(server2, knownTools) {
|
|
|
46256
46315
|
why: import_zod72.z.string().min(3).describe("Plain English. This is what the operator reads before approving."),
|
|
46257
46316
|
targets: import_zod72.z.array(targetSchema).default([]),
|
|
46258
46317
|
creates: import_zod72.z.array(import_zod72.z.object({ ref: import_zod72.z.string(), kind: import_zod72.z.string() })).optional().describe("What this step brings into existence, for later steps to point at."),
|
|
46259
|
-
args: import_zod72.z.record(import_zod72.z.unknown()).optional().describe("The arguments, kept verbatim \u2014 the approval covers these, so changing one voids it.")
|
|
46318
|
+
args: import_zod72.z.record(import_zod72.z.string(), import_zod72.z.unknown()).optional().describe("The arguments, kept verbatim \u2014 the approval covers these, so changing one voids it.")
|
|
46260
46319
|
})).min(1)
|
|
46261
46320
|
},
|
|
46262
46321
|
async ({ goal, findings, steps }) => {
|
|
@@ -46326,7 +46385,7 @@ function registerAgencyBuildTools(server2, knownTools) {
|
|
|
46326
46385
|
undo: import_zod72.z.string().optional().describe("Required on 'applied'. How to reverse this exact write, specifically enough to act on months later."),
|
|
46327
46386
|
because: import_zod72.z.string().optional().describe("Required on 'failed'. What the account actually said."),
|
|
46328
46387
|
result: import_zod72.z.unknown().optional().describe("What came back, kept so a stall can be diagnosed without re-running anything."),
|
|
46329
|
-
resolved: import_zod72.z.record(import_zod72.z.string()).optional().describe("The real ids this step created, keyed by the ref later steps use."),
|
|
46388
|
+
resolved: import_zod72.z.record(import_zod72.z.string(), import_zod72.z.string()).optional().describe("The real ids this step created, keyed by the ref later steps use."),
|
|
46330
46389
|
retry: import_zod72.z.boolean().optional().describe("This is a second run of a step that failed. Both attempts are kept, and the earlier failure stops blocking what came after it.")
|
|
46331
46390
|
},
|
|
46332
46391
|
async ({ buildId, stepId, state, undo, because, result, resolved: resolved2, retry }) => {
|
|
@@ -47191,7 +47250,7 @@ function registerAuditReportTools(server2) {
|
|
|
47191
47250
|
"build_audit_report",
|
|
47192
47251
|
"Turn the output of `audit_workflows` into a client-ready assessment: plain-English findings with money attached where it can honestly be attached, ranked worst-first, plus an explicit list of what could NOT be checked. Call `audit_workflows` first and pass its result here \u2014 this does not re-read the account. Give `leadsPerMonth`, `closeRate` and `monthlyValuePerClient` if you know them and the findings get costed in dollars; leave them out and it says which input is missing instead of guessing. `monthlyValuePerClient` defaults to your saved minimum monthly fee from your agency profile. Use this to produce the document you send or present, not to diagnose \u2014 the diagnosing already happened.",
|
|
47193
47252
|
{
|
|
47194
|
-
audit: import_zod74.z.record(import_zod74.z.unknown()).describe("The full JSON result returned by audit_workflows."),
|
|
47253
|
+
audit: import_zod74.z.record(import_zod74.z.string(), import_zod74.z.unknown()).describe("The full JSON result returned by audit_workflows."),
|
|
47195
47254
|
audience: import_zod74.z.enum(["internal", "prospect"]).optional().describe("Who reads it. 'internal' (default) is a report about an account you already run, and may use your own minimum monthly as what a client is worth. 'prospect' is a document about somebody else's business: your numbers are NEVER used, so findings stay uncosted unless you supply THEIR lead volume, close rate and client value."),
|
|
47196
47255
|
accountName: import_zod74.z.string().describe("The client's name as they should see it on the document."),
|
|
47197
47256
|
locationId: import_zod74.z.string().optional().describe("Sub-account id. Recorded on the document, never shown in the body."),
|
|
@@ -47256,7 +47315,7 @@ function registerAuditReportTools(server2) {
|
|
|
47256
47315
|
"build_site_audit_report",
|
|
47257
47316
|
"Turn a website read-through into the assessment document you put in front of a PROSPECT \u2014 someone whose GoHighLevel account you do not have. Pass the verified observations from a site crawl and it returns plain-English findings, ranked, each one carrying the quote and the page it came from. Use it before a sales call, or alongside a receptionist demo, so the same read of their site fills both. It never invents money: a website cannot tell you what a lead is worth to that business, so findings come back uncosted unless you supply THEIR figures, and the document says why. It also never repeats a claim the crawl could not stand up \u2014 anything refused is returned separately, so you can see what was thrown out rather than wonder what was missed.",
|
|
47258
47317
|
{
|
|
47259
|
-
observations: import_zod74.z.record(import_zod74.z.unknown()).describe("The verified observations payload from the site read: site, pagesRead, observations, limits. Passed through as-is."),
|
|
47318
|
+
observations: import_zod74.z.record(import_zod74.z.string(), import_zod74.z.unknown()).describe("The verified observations payload from the site read: site, pagesRead, observations, limits. Passed through as-is."),
|
|
47260
47319
|
businessName: import_zod74.z.string().describe("The business's name as it should appear on the document."),
|
|
47261
47320
|
leadsPerMonth: import_zod74.z.number().min(0).optional().describe("THEIR leads per month, if they told you. Never your own figure."),
|
|
47262
47321
|
closeRate: import_zod74.z.number().min(0).max(1).optional().describe("THEIR close rate, 0-1, if they told you. Never guessed."),
|
|
@@ -48138,7 +48197,7 @@ var init_brief2 = __esm({
|
|
|
48138
48197
|
preset: presetSchema,
|
|
48139
48198
|
briefSource: briefSourceSchema,
|
|
48140
48199
|
/** Partner-OS deep structures (ICA / offer / brand-DNA), carried verbatim. */
|
|
48141
|
-
extended: import_zod79.z.record(import_zod79.z.unknown()).optional(),
|
|
48200
|
+
extended: import_zod79.z.record(import_zod79.z.string(), import_zod79.z.unknown()).optional(),
|
|
48142
48201
|
business: import_zod79.z.object({
|
|
48143
48202
|
name: import_zod79.z.string(),
|
|
48144
48203
|
type: import_zod79.z.string().optional(),
|
|
@@ -49913,7 +49972,7 @@ async function executeBackbone(plan, deps, opts = {}) {
|
|
|
49913
49972
|
pendStaff(u, `Staff member "${name}" (${u.email}) was not created: no agency key is registered (creating users is an agency-level write). Run register_agency_key with an agency-level Private Integration key that has users.write, then re-run \u2014 or add them in Settings \u2192 Team and re-run to bind them by email.`);
|
|
49914
49973
|
continue;
|
|
49915
49974
|
}
|
|
49916
|
-
let outcome;
|
|
49975
|
+
let outcome = void 0;
|
|
49917
49976
|
let recoveredUser;
|
|
49918
49977
|
try {
|
|
49919
49978
|
outcome = await deps.createUser({ firstName: u.firstName, lastName: u.lastName, email: u.email, role: u.role, phone: u.phone });
|
|
@@ -50927,6 +50986,7 @@ function pickCalendarPins(workflow) {
|
|
|
50927
50986
|
}
|
|
50928
50987
|
function makeFirebaseRequest(builderClient) {
|
|
50929
50988
|
return async (url, method, body) => {
|
|
50989
|
+
assertWriteAllowed(method, url, { defaultLocationId: builderClient.locationId });
|
|
50930
50990
|
const headers = await builderClient.buildHeaders();
|
|
50931
50991
|
const response = await fetch(url, { method, headers, body: body ? JSON.stringify(body) : void 0 });
|
|
50932
50992
|
if (!response.ok) {
|
|
@@ -51353,14 +51413,14 @@ function registerIntakeToBuildTools(server2, client, builderClient, registry2) {
|
|
|
51353
51413
|
"validate_brief",
|
|
51354
51414
|
"Validate an Intake-to-Build Brief object against the \xA74 schema. Returns {valid, errors}. Use to check a normalized brief before handing it to the plan-generation skill.",
|
|
51355
51415
|
{
|
|
51356
|
-
brief: import_zod80.z.record(import_zod80.z.unknown()).describe("The Brief object to validate.")
|
|
51416
|
+
brief: import_zod80.z.record(import_zod80.z.string(), import_zod80.z.unknown()).describe("The Brief object to validate.")
|
|
51357
51417
|
},
|
|
51358
51418
|
async ({ brief }) => validateBrief(brief)
|
|
51359
51419
|
);
|
|
51360
51420
|
const buildPlanSchema2 = {
|
|
51361
|
-
plan: import_zod80.z.record(import_zod80.z.unknown()).optional().describe("The approved \xA75 Build Plan object. Omit it and pass useSavedPlan:true to build from the plan already saved for this account."),
|
|
51421
|
+
plan: import_zod80.z.record(import_zod80.z.string(), import_zod80.z.unknown()).optional().describe("The approved \xA75 Build Plan object. Omit it and pass useSavedPlan:true to build from the plan already saved for this account."),
|
|
51362
51422
|
fromPreset: import_zod80.z.boolean().optional().describe("PLAN-FROM-PRESET (PRD row 4d): compose the \xA75 plan SERVER-SIDE from the industry preset + the \xA74 brief, deterministically, in milliseconds \u2014 never author workflow actions by hand when a preset covers the brief. Pass `brief` (required) and optionally `preset` and `copy`; omit `plan`. The composed plan carries the preset's full structure (workflows, templates with complete send-ready copy token-filled from the brief, staff users, calendars with the brief's appointment lengths, pipeline stages, notification targets) and then flows through the exact same validate \u2192 dry_run/execute path as a passed plan, including being saved on execute. The dry_run response lists copySlots \u2014 the template refs whose subject/body the model MAY refine via `copy` on the execute call. If the preset cannot cover the brief the tool answers phase:\"compose\" with the reasons; only then author a plan by hand."),
|
|
51363
|
-
brief: import_zod80.z.record(import_zod80.z.unknown()).optional().describe("fromPreset only: the \xA74 Brief (from normalize_submission_to_brief, or assembled from the cockpit intake answers)."),
|
|
51423
|
+
brief: import_zod80.z.record(import_zod80.z.string(), import_zod80.z.unknown()).optional().describe("fromPreset only: the \xA74 Brief (from normalize_submission_to_brief, or assembled from the cockpit intake answers)."),
|
|
51364
51424
|
preset: import_zod80.z.string().optional().describe("fromPreset only: which preset to compose from \u2014 a preset id (med_spa, clinic, coach, local_service, ecom, generic), an alias (dental, medspa, home_services\u2026), or an industry slug (med-spa, local-service, ecommerce\u2026). Defaults to the brief's own preset field; an unknown value falls back to the generic preset (noted in the response)."),
|
|
51365
51425
|
copy: import_zod80.z.array(
|
|
51366
51426
|
import_zod80.z.object({
|
|
@@ -52013,7 +52073,7 @@ function registerIntakeToBuildTools(server2, client, builderClient, registry2) {
|
|
|
52013
52073
|
{
|
|
52014
52074
|
formId: import_zod80.z.string().describe("The intake form ID (from install_intake_form)."),
|
|
52015
52075
|
submissionId: import_zod80.z.string().optional().describe("Specific submission to normalize. Defaults to the most recent."),
|
|
52016
|
-
fieldMap: import_zod80.z.record(import_zod80.z.string()).optional().describe("intakeKey -> customFieldId map from install_intake_form. Reconstructed from the form if omitted."),
|
|
52076
|
+
fieldMap: import_zod80.z.record(import_zod80.z.string(), import_zod80.z.string()).optional().describe("intakeKey -> customFieldId map from install_intake_form. Reconstructed from the form if omitted."),
|
|
52017
52077
|
preset: import_zod80.z.string().optional().describe("Override the preset. Defaults to one derived from business_type.")
|
|
52018
52078
|
},
|
|
52019
52079
|
async ({ formId, submissionId, fieldMap, preset }) => {
|
|
@@ -52100,6 +52160,7 @@ var init_intake_to_build = __esm({
|
|
|
52100
52160
|
init_plan_from_preset();
|
|
52101
52161
|
init_executor();
|
|
52102
52162
|
init_execute();
|
|
52163
|
+
init_account_rules();
|
|
52103
52164
|
customFieldItemSchema = import_zod80.z.object({
|
|
52104
52165
|
id: import_zod80.z.string(),
|
|
52105
52166
|
name: import_zod80.z.string(),
|
|
@@ -52145,7 +52206,7 @@ function registerAllTools(server2, client, registry2, mcpVersion, env = process.
|
|
|
52145
52206
|
registerConnectorTools(wrap(CONNECTORS_MODULE));
|
|
52146
52207
|
registerGoogleCalendarTools(wrap(GOOGLE_CALENDAR_MODULE));
|
|
52147
52208
|
registerDeliveryAuditTools(wrap(DELIVERY_AUDIT_MODULE));
|
|
52148
|
-
registerDesignStudioTools(wrap(DESIGN_STUDIO_MODULE)
|
|
52209
|
+
registerDesignStudioTools(wrap(DESIGN_STUDIO_MODULE));
|
|
52149
52210
|
registerAgencyBuildTools(wrap(AGENCY_BUILD_MODULE), () => registeredTools);
|
|
52150
52211
|
registerClientEngagementTools(wrap(CLIENT_ENGAGEMENTS_MODULE), registry2);
|
|
52151
52212
|
registerAuditReportTools(wrap(AUDIT_REPORT_MODULE));
|
|
@@ -52291,9 +52352,16 @@ var init_tools = __esm({
|
|
|
52291
52352
|
[registerAssociationTools, "associations"],
|
|
52292
52353
|
[registerEstimateTools, "estimates"],
|
|
52293
52354
|
[registerCouponTools, "coupons"],
|
|
52294
|
-
[registerKnowledgeBaseTools, "knowledge-base"],
|
|
52295
|
-
|
|
52296
|
-
|
|
52355
|
+
[(server2, client) => registerKnowledgeBaseTools(server2, client), "knowledge-base"],
|
|
52356
|
+
// Wrapped, not listed bare: this registrar's third parameter is a `deps`
|
|
52357
|
+
// object, not the TokenRegistry the loop below passes to everything in this
|
|
52358
|
+
// list. Today that is harmless — every key of `deps` is optional and a
|
|
52359
|
+
// TokenRegistry has none of them, so every read falls back to its default —
|
|
52360
|
+
// but it is harmless by luck, and stops being harmless the day TokenRegistry
|
|
52361
|
+
// grows a `sleep`, `now` or `journalDir`. The wrapper passes nothing, which
|
|
52362
|
+
// is what the registrar effectively receives now.
|
|
52363
|
+
[(server2, client) => registerConversationAiTools(server2, client), "conversation-ai"],
|
|
52364
|
+
[(server2, client) => registerAiEmployeeDemoTools(server2, client), "ai-employee-demo"],
|
|
52297
52365
|
[registerVoiceAiTools, "voice-ai"],
|
|
52298
52366
|
[registerDocumentTools, "documents"],
|
|
52299
52367
|
[registerBulkOperationTools, "bulk-operations"],
|
|
@@ -52542,6 +52610,12 @@ var init_cli2 = __esm({
|
|
|
52542
52610
|
});
|
|
52543
52611
|
|
|
52544
52612
|
// src/index.ts
|
|
52613
|
+
var index_exports = {};
|
|
52614
|
+
__export(index_exports, {
|
|
52615
|
+
TRANSITIONAL_ATTESTATION_DEADLINE: () => TRANSITIONAL_ATTESTATION_DEADLINE,
|
|
52616
|
+
licenseGateFailureMessage: () => licenseGateFailureMessage
|
|
52617
|
+
});
|
|
52618
|
+
module.exports = __toCommonJS(index_exports);
|
|
52545
52619
|
var dotenv2 = __toESM(require("dotenv"));
|
|
52546
52620
|
var path38 = __toESM(require("path"));
|
|
52547
52621
|
var fs39 = __toESM(require("fs"));
|
|
@@ -52622,6 +52696,15 @@ var os6 = __toESM(require("node:os"));
|
|
|
52622
52696
|
function sha2562(buf) {
|
|
52623
52697
|
return (0, import_node_crypto12.createHash)("sha256").update(buf).digest("hex");
|
|
52624
52698
|
}
|
|
52699
|
+
function isOlderRelease(candidate, installed) {
|
|
52700
|
+
const parts = (v) => v.trim().split(".").map((n) => Number.parseInt(n, 10));
|
|
52701
|
+
const a = parts(candidate), b = parts(installed);
|
|
52702
|
+
if (a.length !== 3 || b.length !== 3 || [...a, ...b].some(Number.isNaN)) return false;
|
|
52703
|
+
for (let i = 0; i < 3; i++) {
|
|
52704
|
+
if (a[i] !== b[i]) return a[i] < b[i];
|
|
52705
|
+
}
|
|
52706
|
+
return false;
|
|
52707
|
+
}
|
|
52625
52708
|
function bundledSkillsDir(baseDir) {
|
|
52626
52709
|
const candidate = path37.resolve(baseDir, "..", "skills");
|
|
52627
52710
|
try {
|
|
@@ -52655,6 +52738,7 @@ function installBundledSkills(opts) {
|
|
|
52655
52738
|
installed: [],
|
|
52656
52739
|
updated: [],
|
|
52657
52740
|
skippedUserModified: [],
|
|
52741
|
+
skippedOlderPackage: [],
|
|
52658
52742
|
unchanged: [],
|
|
52659
52743
|
errors: [],
|
|
52660
52744
|
targetDir: opts.targetDir ?? path37.join(os6.homedir(), ".claude", "skills"),
|
|
@@ -52697,7 +52781,11 @@ function installBundledSkills(opts) {
|
|
|
52697
52781
|
continue;
|
|
52698
52782
|
}
|
|
52699
52783
|
const lastWritten = marker.files[rel];
|
|
52700
|
-
if (lastWritten && destHash === lastWritten) {
|
|
52784
|
+
if (lastWritten && destHash === lastWritten && isOlderRelease(opts.packageVersion, marker.packageVersion)) {
|
|
52785
|
+
newMarker.files[rel] = destHash;
|
|
52786
|
+
newMarker.packageVersion = marker.packageVersion;
|
|
52787
|
+
result.skippedOlderPackage.push(rel);
|
|
52788
|
+
} else if (lastWritten && destHash === lastWritten) {
|
|
52701
52789
|
fs38.writeFileSync(dest, src);
|
|
52702
52790
|
newMarker.files[rel] = srcHash;
|
|
52703
52791
|
result.updated.push(rel);
|
|
@@ -52721,6 +52809,7 @@ function summarizeInstall(r) {
|
|
|
52721
52809
|
if (r.installed.length) bits.push(`${r.installed.length} installed`);
|
|
52722
52810
|
if (r.updated.length) bits.push(`${r.updated.length} updated`);
|
|
52723
52811
|
if (r.skippedUserModified.length) bits.push(`${r.skippedUserModified.length} kept (your edits)`);
|
|
52812
|
+
if (r.skippedOlderPackage.length) bits.push(`${r.skippedOlderPackage.length} kept (newer than this version)`);
|
|
52724
52813
|
if (r.errors.length) bits.push(`${r.errors.length} errors`);
|
|
52725
52814
|
if (bits.length === 0) return "";
|
|
52726
52815
|
return `Skills: ${bits.join(", ")} \u2192 ${r.targetDir}`;
|
|
@@ -52750,6 +52839,9 @@ function registerSkillsTool(server2, packageVersion, baseDir) {
|
|
|
52750
52839
|
installed: result.installed,
|
|
52751
52840
|
updated: result.updated,
|
|
52752
52841
|
keptYourEdits: result.skippedUserModified,
|
|
52842
|
+
// A member running an older install alongside a newer one would otherwise
|
|
52843
|
+
// have their skills rolled back without a word. BUG-2026-09-10-01.
|
|
52844
|
+
keptNewerThanThisVersion: result.skippedOlderPackage,
|
|
52753
52845
|
unchanged: result.unchanged.length,
|
|
52754
52846
|
errors: result.errors,
|
|
52755
52847
|
next: result.installed.length > 0 || result.updated.length > 0 ? "Fully restart Claude (quit and reopen) so the new/updated skills load." : "Everything already current."
|
|
@@ -52909,6 +53001,12 @@ registerMetaTools(server, pkg.version);
|
|
|
52909
53001
|
var inBootstrapMode = true;
|
|
52910
53002
|
var telemetryTier = "none";
|
|
52911
53003
|
var TRANSITIONAL_ATTESTATION_DEADLINE = Date.parse("2026-06-15T00:00:00.000Z");
|
|
53004
|
+
function licenseGateFailureMessage(lic, deadline = TRANSITIONAL_ATTESTATION_DEADLINE) {
|
|
53005
|
+
if (!lic.ok) return `[ghl-mcp] License check failed: ${lic.error}
|
|
53006
|
+
`;
|
|
53007
|
+
return `[ghl-mcp] Your license is valid, but our license server did not return a signed attestation, which has been required since ${new Date(deadline).toISOString().slice(0, 10)}. This is on our side, not your install. Please write support@ghlcommand.com.
|
|
53008
|
+
`;
|
|
53009
|
+
}
|
|
52912
53010
|
async function renewAttestation(creds) {
|
|
52913
53011
|
if (!creds.email || !creds.license_key) return "unauthorized";
|
|
52914
53012
|
let lic;
|
|
@@ -52991,7 +53089,9 @@ async function resolveAccessAndRegister() {
|
|
|
52991
53089
|
attestationStatus = "renewed-from-legacy";
|
|
52992
53090
|
} else {
|
|
52993
53091
|
attestationStatus = "needs-reset";
|
|
52994
|
-
if (renewed === "
|
|
53092
|
+
if (renewed === "install_limit") {
|
|
53093
|
+
process.stderr.write("[ghl-mcp] Bootstrap mode. Free a device on your license, or write support@ghlcommand.com.\n");
|
|
53094
|
+
} else if (renewed === "cancelled" || renewed === "unauthorized") {
|
|
52995
53095
|
process.stderr.write(`[ghl-mcp] Server says license is ${renewed}. Bootstrap mode.
|
|
52996
53096
|
`);
|
|
52997
53097
|
} else if (renewed === "renewed-unsigned") {
|
|
@@ -53028,8 +53128,7 @@ async function resolveAccessAndRegister() {
|
|
|
53028
53128
|
} catch {
|
|
53029
53129
|
}
|
|
53030
53130
|
} else {
|
|
53031
|
-
process.stderr.write(
|
|
53032
|
-
`);
|
|
53131
|
+
process.stderr.write(licenseGateFailureMessage(lic));
|
|
53033
53132
|
}
|
|
53034
53133
|
} else {
|
|
53035
53134
|
process.stderr.write(
|
|
@@ -53231,3 +53330,8 @@ main().catch((error) => {
|
|
|
53231
53330
|
process.stderr.write(`[ghl-mcp] Failed to start: ${error}
|
|
53232
53331
|
`);
|
|
53233
53332
|
});
|
|
53333
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
53334
|
+
0 && (module.exports = {
|
|
53335
|
+
TRANSITIONAL_ATTESTATION_DEADLINE,
|
|
53336
|
+
licenseGateFailureMessage
|
|
53337
|
+
});
|