@elitedcs/ghl-mcp 3.83.8 → 3.83.9
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 +50 -0
- package/README.md +6 -6
- package/dist/index.js +660 -484
- package/guide/guide.html +4 -4
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -443,13 +443,13 @@ function setProtectedAccount(locationId2, name, file2 = accountRulesPath()) {
|
|
|
443
443
|
function isWriteMethod(method) {
|
|
444
444
|
return WRITE_METHODS.has(method.toUpperCase());
|
|
445
445
|
}
|
|
446
|
-
function locationIdsInRequest(
|
|
446
|
+
function locationIdsInRequest(path40, opts = {}) {
|
|
447
447
|
const found = /* @__PURE__ */ new Set();
|
|
448
448
|
const add = (v) => {
|
|
449
449
|
if (typeof v === "string" && v.trim()) found.add(v.trim());
|
|
450
450
|
else if (Array.isArray(v)) v.forEach(add);
|
|
451
451
|
};
|
|
452
|
-
const seg = /\/locations\/([^/?#]+)/i.exec(
|
|
452
|
+
const seg = /\/locations\/([^/?#]+)/i.exec(path40);
|
|
453
453
|
if (seg) add(decodeURIComponent(seg[1]));
|
|
454
454
|
for (const src of [opts.params, opts.body]) {
|
|
455
455
|
if (!src || typeof src !== "object") continue;
|
|
@@ -476,7 +476,7 @@ function protectedRefusalFor(locationId2, rulesFile) {
|
|
|
476
476
|
const name = rules.protected[id];
|
|
477
477
|
return name ? protectedRefusalMessage(name, false) : null;
|
|
478
478
|
}
|
|
479
|
-
function assertWriteAllowed(method,
|
|
479
|
+
function assertWriteAllowed(method, path40, opts = {}) {
|
|
480
480
|
if (!isWriteMethod(method)) return;
|
|
481
481
|
let rules;
|
|
482
482
|
try {
|
|
@@ -485,7 +485,7 @@ function assertWriteAllowed(method, path39, opts = {}) {
|
|
|
485
485
|
throw new Error(UNREADABLE_RULES_MSG);
|
|
486
486
|
}
|
|
487
487
|
if (Object.keys(rules.protected).length === 0) return;
|
|
488
|
-
for (const id of locationIdsInRequest(
|
|
488
|
+
for (const id of locationIdsInRequest(path40, opts)) {
|
|
489
489
|
const name = rules.protected[id];
|
|
490
490
|
if (name) throw new Error(protectedRefusalMessage(name, false));
|
|
491
491
|
}
|
|
@@ -552,6 +552,10 @@ function requestError(error) {
|
|
|
552
552
|
function isGHLRequestError(error) {
|
|
553
553
|
return error !== null && typeof error === "object" && requestErrors.has(error);
|
|
554
554
|
}
|
|
555
|
+
function keyHint(key) {
|
|
556
|
+
if (!key) return "NOT SET";
|
|
557
|
+
return key.length > 8 ? `ending \u2026${key.slice(-4)}` : "set";
|
|
558
|
+
}
|
|
555
559
|
var GHL_BASE_URL, GHL_API_VERSION, MAX_RETRIES, BASE_DELAY_MS, requestErrors, GHLClient;
|
|
556
560
|
var init_ghl_client = __esm({
|
|
557
561
|
"src/ghl-client.ts"() {
|
|
@@ -587,10 +591,10 @@ var init_ghl_client = __esm({
|
|
|
587
591
|
this.apiKey = apiKey2;
|
|
588
592
|
}
|
|
589
593
|
/**
|
|
590
|
-
*
|
|
594
|
+
* Which key is loaded, for diagnostics — never the key itself. See keyHint.
|
|
591
595
|
*/
|
|
592
|
-
|
|
593
|
-
return this.apiKey
|
|
596
|
+
getApiKeyHint() {
|
|
597
|
+
return keyHint(this.apiKey);
|
|
594
598
|
}
|
|
595
599
|
/**
|
|
596
600
|
* Return the current API key. Only used internally by switch_location to
|
|
@@ -607,8 +611,8 @@ var init_ghl_client = __esm({
|
|
|
607
611
|
Version: version || GHL_API_VERSION
|
|
608
612
|
};
|
|
609
613
|
}
|
|
610
|
-
buildUrl(
|
|
611
|
-
const url = new URL(
|
|
614
|
+
buildUrl(path40, params) {
|
|
615
|
+
const url = new URL(path40, GHL_BASE_URL);
|
|
612
616
|
if (params) {
|
|
613
617
|
for (const [key, value] of Object.entries(params)) {
|
|
614
618
|
if (value !== void 0 && value !== null) {
|
|
@@ -618,18 +622,18 @@ var init_ghl_client = __esm({
|
|
|
618
622
|
}
|
|
619
623
|
return url.toString();
|
|
620
624
|
}
|
|
621
|
-
async request(method,
|
|
625
|
+
async request(method, path40, options = {}, attempt = 0) {
|
|
622
626
|
if (inBuildRequestScope() && method === "GET" && !inAccountReadAttempt()) {
|
|
623
|
-
return retryAccountRead(() => this.request(method,
|
|
627
|
+
return retryAccountRead(() => this.request(method, path40, options, attempt), `${method} ${path40}`);
|
|
624
628
|
}
|
|
625
629
|
if (attempt === 0) {
|
|
626
|
-
assertWriteAllowed(method,
|
|
630
|
+
assertWriteAllowed(method, path40, {
|
|
627
631
|
params: options.params,
|
|
628
632
|
body: options.body,
|
|
629
633
|
defaultLocationId: this.defaultLocationId
|
|
630
634
|
});
|
|
631
635
|
}
|
|
632
|
-
const url = this.buildUrl(
|
|
636
|
+
const url = this.buildUrl(path40, options.params);
|
|
633
637
|
const headers = this.buildHeaders(options.version);
|
|
634
638
|
const fetchOptions = {
|
|
635
639
|
method,
|
|
@@ -647,14 +651,14 @@ var init_ghl_client = __esm({
|
|
|
647
651
|
} catch (error) {
|
|
648
652
|
clearTimeout(timeout);
|
|
649
653
|
if (error instanceof Error && error.name === "AbortError") {
|
|
650
|
-
throw requestError(new Error(`Request timeout (30s): ${method} ${
|
|
654
|
+
throw requestError(new Error(`Request timeout (30s): ${method} ${path40}`));
|
|
651
655
|
}
|
|
652
656
|
if (!inBuildRequestScope() && !options.noRetry && attempt < MAX_RETRIES) {
|
|
653
657
|
const delay5 = computeRetryDelay(null, attempt, BASE_DELAY_MS);
|
|
654
|
-
process.stderr.write(`[ghl-mcp] Network error on ${method} ${
|
|
658
|
+
process.stderr.write(`[ghl-mcp] Network error on ${method} ${path40}, retry ${attempt + 1}/${MAX_RETRIES} in ${delay5}ms
|
|
655
659
|
`);
|
|
656
660
|
await new Promise((r) => setTimeout(r, delay5));
|
|
657
|
-
return this.request(method,
|
|
661
|
+
return this.request(method, path40, options, attempt + 1);
|
|
658
662
|
}
|
|
659
663
|
throw requestError(error);
|
|
660
664
|
} finally {
|
|
@@ -662,10 +666,10 @@ var init_ghl_client = __esm({
|
|
|
662
666
|
}
|
|
663
667
|
if (!inBuildRequestScope() && !options.noRetry && (response.status === 429 || response.status >= 500) && attempt < MAX_RETRIES) {
|
|
664
668
|
const delay5 = computeRetryDelay(response.headers.get("Retry-After"), attempt, BASE_DELAY_MS);
|
|
665
|
-
process.stderr.write(`[ghl-mcp] ${response.status} on ${method} ${
|
|
669
|
+
process.stderr.write(`[ghl-mcp] ${response.status} on ${method} ${path40}, retry ${attempt + 1}/${MAX_RETRIES} in ${delay5}ms
|
|
666
670
|
`);
|
|
667
671
|
await new Promise((r) => setTimeout(r, delay5));
|
|
668
|
-
return this.request(method,
|
|
672
|
+
return this.request(method, path40, options, attempt + 1);
|
|
669
673
|
}
|
|
670
674
|
if (!response.ok) {
|
|
671
675
|
let errorBody = "";
|
|
@@ -674,7 +678,7 @@ var init_ghl_client = __esm({
|
|
|
674
678
|
} catch {
|
|
675
679
|
}
|
|
676
680
|
throw requestError(new Error(
|
|
677
|
-
`GHL API Error ${response.status} ${response.statusText}: ${method} ${
|
|
681
|
+
`GHL API Error ${response.status} ${response.statusText}: ${method} ${path40}
|
|
678
682
|
${errorBody}`
|
|
679
683
|
));
|
|
680
684
|
}
|
|
@@ -691,20 +695,20 @@ ${errorBody}`
|
|
|
691
695
|
return { message: text };
|
|
692
696
|
}
|
|
693
697
|
}
|
|
694
|
-
async get(
|
|
695
|
-
return this.request("GET",
|
|
698
|
+
async get(path40, options) {
|
|
699
|
+
return this.request("GET", path40, options);
|
|
696
700
|
}
|
|
697
|
-
async post(
|
|
698
|
-
return this.request("POST",
|
|
701
|
+
async post(path40, options) {
|
|
702
|
+
return this.request("POST", path40, options);
|
|
699
703
|
}
|
|
700
|
-
async put(
|
|
701
|
-
return this.request("PUT",
|
|
704
|
+
async put(path40, options) {
|
|
705
|
+
return this.request("PUT", path40, options);
|
|
702
706
|
}
|
|
703
|
-
async patch(
|
|
704
|
-
return this.request("PATCH",
|
|
707
|
+
async patch(path40, options) {
|
|
708
|
+
return this.request("PATCH", path40, options);
|
|
705
709
|
}
|
|
706
|
-
async delete(
|
|
707
|
-
return this.request("DELETE",
|
|
710
|
+
async delete(path40, options) {
|
|
711
|
+
return this.request("DELETE", path40, options);
|
|
708
712
|
}
|
|
709
713
|
/**
|
|
710
714
|
* Helper: resolves locationId from args or falls back to default
|
|
@@ -7467,11 +7471,11 @@ function registerEmailTools(server2, client) {
|
|
|
7467
7471
|
function registerEmailBuilderInternalTools(server2, builderClient, publicClient) {
|
|
7468
7472
|
if (!builderClient) return;
|
|
7469
7473
|
const client = builderClient;
|
|
7470
|
-
async function builderRequest2(method,
|
|
7471
|
-
const response = await builderRequest(client, method, `${EMAIL_BUILDER_BASE}${
|
|
7474
|
+
async function builderRequest2(method, path40, body) {
|
|
7475
|
+
const response = await builderRequest(client, method, `${EMAIL_BUILDER_BASE}${path40}`, body ? { body } : {});
|
|
7472
7476
|
if (!response.ok) {
|
|
7473
7477
|
const text2 = await response.text();
|
|
7474
|
-
throw new Error(`Email Builder API Error ${response.status}: ${method} /emails/builder${
|
|
7478
|
+
throw new Error(`Email Builder API Error ${response.status}: ${method} /emails/builder${path40}
|
|
7475
7479
|
${text2}`);
|
|
7476
7480
|
}
|
|
7477
7481
|
const text = await response.text();
|
|
@@ -11185,11 +11189,11 @@ async function readPageFull(client, pageId, fetcher = fetch) {
|
|
|
11185
11189
|
const headers = await client.buildHeaders();
|
|
11186
11190
|
headers.Origin = "https://app.gohighlevel.com";
|
|
11187
11191
|
headers.Referer = "https://app.gohighlevel.com/";
|
|
11188
|
-
const
|
|
11189
|
-
const response = await fetcher(`https://backend.leadconnectorhq.com/funnels${
|
|
11192
|
+
const path40 = `/page/${pageId}?locationId=${client.locationId}`;
|
|
11193
|
+
const response = await fetcher(`https://backend.leadconnectorhq.com/funnels${path40}`, { method: "GET", headers });
|
|
11190
11194
|
if (!response.ok) {
|
|
11191
11195
|
const text2 = await response.text();
|
|
11192
|
-
throw new Error(`Funnel API Error ${response.status}: GET ${
|
|
11196
|
+
throw new Error(`Funnel API Error ${response.status}: GET ${path40}
|
|
11193
11197
|
${text2}`);
|
|
11194
11198
|
}
|
|
11195
11199
|
const text = await response.text();
|
|
@@ -11248,20 +11252,20 @@ function registerFunnelBuilderTools(server2, builderClient) {
|
|
|
11248
11252
|
return;
|
|
11249
11253
|
}
|
|
11250
11254
|
const client = builderClient;
|
|
11251
|
-
async function internalGet(
|
|
11252
|
-
return client.request("GET",
|
|
11255
|
+
async function internalGet(path40) {
|
|
11256
|
+
return client.request("GET", path40);
|
|
11253
11257
|
}
|
|
11254
|
-
async function internalPost(
|
|
11255
|
-
return client.request("POST",
|
|
11258
|
+
async function internalPost(path40, body) {
|
|
11259
|
+
return client.request("POST", path40, body);
|
|
11256
11260
|
}
|
|
11257
|
-
async function internalPut(
|
|
11258
|
-
return client.request("PUT",
|
|
11261
|
+
async function internalPut(path40, body) {
|
|
11262
|
+
return client.request("PUT", path40, body);
|
|
11259
11263
|
}
|
|
11260
|
-
async function internalDelete(
|
|
11261
|
-
return client.request("DELETE",
|
|
11264
|
+
async function internalDelete(path40) {
|
|
11265
|
+
return client.request("DELETE", path40);
|
|
11262
11266
|
}
|
|
11263
|
-
async function funnelRequest(method,
|
|
11264
|
-
const url = `https://backend.leadconnectorhq.com/funnels${
|
|
11267
|
+
async function funnelRequest(method, path40, body) {
|
|
11268
|
+
const url = `https://backend.leadconnectorhq.com/funnels${path40}`;
|
|
11265
11269
|
const sendsBody = body !== void 0 && (method === "POST" || method === "PUT");
|
|
11266
11270
|
const response = await builderRequest(client, method, url, {
|
|
11267
11271
|
...sendsBody ? { body } : {},
|
|
@@ -11274,7 +11278,7 @@ function registerFunnelBuilderTools(server2, builderClient) {
|
|
|
11274
11278
|
});
|
|
11275
11279
|
if (!response.ok) {
|
|
11276
11280
|
const text2 = await response.text();
|
|
11277
|
-
throw new Error(`Funnel API Error ${response.status}: ${method} ${
|
|
11281
|
+
throw new Error(`Funnel API Error ${response.status}: ${method} ${path40}
|
|
11278
11282
|
${text2}`);
|
|
11279
11283
|
}
|
|
11280
11284
|
const text = await response.text();
|
|
@@ -11850,17 +11854,17 @@ function requireCompany(api) {
|
|
|
11850
11854
|
}
|
|
11851
11855
|
return company;
|
|
11852
11856
|
}
|
|
11853
|
-
async function call(api,
|
|
11857
|
+
async function call(api, path40, init) {
|
|
11854
11858
|
const headers = { ...await api.headers(), Origin: "https://app.gohighlevel.com", Referer: "https://app.gohighlevel.com/" };
|
|
11855
|
-
if (MUTATING_PATHS.some((mutating) =>
|
|
11856
|
-
assertWriteAllowed("POST", SVC +
|
|
11859
|
+
if (MUTATING_PATHS.some((mutating) => path40.startsWith(mutating))) {
|
|
11860
|
+
assertWriteAllowed("POST", SVC + path40, { defaultLocationId: api.locationId() });
|
|
11857
11861
|
}
|
|
11858
11862
|
const doFetch = api.fetch ?? fetch;
|
|
11859
|
-
const response = await doFetch(SVC +
|
|
11863
|
+
const response = await doFetch(SVC + path40, { ...init, headers });
|
|
11860
11864
|
const text = await response.text();
|
|
11861
11865
|
if (!response.ok) {
|
|
11862
11866
|
const hint = response.status === 401 && /not authorized to access this company/i.test(text) ? " \u2014 the Firebase session is authenticated as a DIFFERENT company than the one that owns this sub-account. Run switch_location for this sub-account and try again." : response.status === 401 ? " \u2014 the template library needs the workflow-builder Firebase session, not the API key alone. Run enable_workflow_builder for this sub-account." : "";
|
|
11863
|
-
throw new Error(`GoHighLevel template library ${response.status} on ${
|
|
11867
|
+
throw new Error(`GoHighLevel template library ${response.status} on ${path40}${hint}
|
|
11864
11868
|
${text.slice(0, 300)}`);
|
|
11865
11869
|
}
|
|
11866
11870
|
return text ? JSON.parse(text) : {};
|
|
@@ -16506,8 +16510,8 @@ function registerPageStudioTools(server2, builderClient) {
|
|
|
16506
16510
|
}
|
|
16507
16511
|
return { map: out, truncated };
|
|
16508
16512
|
}
|
|
16509
|
-
async function funnelRequest(method,
|
|
16510
|
-
const response = await builderRequest(client, method, `https://backend.leadconnectorhq.com/funnels${
|
|
16513
|
+
async function funnelRequest(method, path40, body) {
|
|
16514
|
+
const response = await builderRequest(client, method, `https://backend.leadconnectorhq.com/funnels${path40}`, {
|
|
16511
16515
|
...body ? { body } : {},
|
|
16512
16516
|
headers: {
|
|
16513
16517
|
// Internal-API calls require the app origin (autosave needs the
|
|
@@ -16516,7 +16520,7 @@ function registerPageStudioTools(server2, builderClient) {
|
|
|
16516
16520
|
Referer: "https://app.gohighlevel.com/"
|
|
16517
16521
|
}
|
|
16518
16522
|
});
|
|
16519
|
-
if (!response.ok) throw new Error(`Funnel API Error ${response.status}: ${method} ${
|
|
16523
|
+
if (!response.ok) throw new Error(`Funnel API Error ${response.status}: ${method} ${path40}
|
|
16520
16524
|
${await response.text()}`);
|
|
16521
16525
|
const text = await response.text();
|
|
16522
16526
|
return text ? JSON.parse(text) : {};
|
|
@@ -17120,13 +17124,13 @@ function cleanFormDataForSave(formData) {
|
|
|
17120
17124
|
const cleaned = dropRedundantCaseCollidingKeys(formData);
|
|
17121
17125
|
return isJsonObject(cleaned) ? cleaned : formData;
|
|
17122
17126
|
}
|
|
17123
|
-
async function formApiRequest(client, method,
|
|
17124
|
-
const url = `https://backend.leadconnectorhq.com/forms${
|
|
17127
|
+
async function formApiRequest(client, method, path40, body) {
|
|
17128
|
+
const url = `https://backend.leadconnectorhq.com/forms${path40}`;
|
|
17125
17129
|
const sendsBody = body !== void 0 && (method === "POST" || method === "PUT");
|
|
17126
17130
|
const response = await builderRequest(client, method, url, sendsBody ? { body } : {});
|
|
17127
17131
|
if (!response.ok) {
|
|
17128
17132
|
const text2 = await response.text();
|
|
17129
|
-
throw new Error(`Form API Error ${response.status}: ${method} ${
|
|
17133
|
+
throw new Error(`Form API Error ${response.status}: ${method} ${path40}
|
|
17130
17134
|
${text2}`);
|
|
17131
17135
|
}
|
|
17132
17136
|
const text = await response.text();
|
|
@@ -17168,7 +17172,7 @@ function registerFormBuilderTools(server2, builderClient, publicClient) {
|
|
|
17168
17172
|
);
|
|
17169
17173
|
return;
|
|
17170
17174
|
}
|
|
17171
|
-
const formRequest = (method,
|
|
17175
|
+
const formRequest = (method, path40, body) => formApiRequest(client, method, path40, body);
|
|
17172
17176
|
server2.tool(
|
|
17173
17177
|
"get_form_full",
|
|
17174
17178
|
"Get a form with full builder data: all fields (labels, types, IDs, validation), conditional logic, auto-responder config, email notification settings, styling, and version history. This is the internal API \u2014 it returns what the form builder UI shows, with one exception: where GoHighLevel stores the same value under two spellings of one key that differ only in case (it writes custom fields with both `Id` and `id`), only the lowercase one is returned. No value is lost, and the result can be edited and passed straight back to update_form.",
|
|
@@ -17302,10 +17306,10 @@ function registerFormBuilderTools(server2, builderClient, publicClient) {
|
|
|
17302
17306
|
},
|
|
17303
17307
|
async ({ formId, limit, skip }) => {
|
|
17304
17308
|
try {
|
|
17305
|
-
let
|
|
17306
|
-
if (formId)
|
|
17307
|
-
if (skip)
|
|
17308
|
-
const result = await formRequest("GET",
|
|
17309
|
+
let path40 = `/submissions?locationId=${client.locationId}&limit=${limit ?? 20}`;
|
|
17310
|
+
if (formId) path40 += `&formId=${formId}`;
|
|
17311
|
+
if (skip) path40 += `&skip=${skip}`;
|
|
17312
|
+
const result = await formRequest("GET", path40);
|
|
17309
17313
|
return {
|
|
17310
17314
|
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
17311
17315
|
};
|
|
@@ -17713,13 +17717,13 @@ var init_funnel_qa = __esm({
|
|
|
17713
17717
|
function registerPipelineBuilderTools(server2, builderClient) {
|
|
17714
17718
|
if (!builderClient) return;
|
|
17715
17719
|
const client = builderClient;
|
|
17716
|
-
async function pipelineRequest(method,
|
|
17717
|
-
const url = `https://backend.leadconnectorhq.com/opportunities/pipelines${
|
|
17720
|
+
async function pipelineRequest(method, path40, body) {
|
|
17721
|
+
const url = `https://backend.leadconnectorhq.com/opportunities/pipelines${path40}`;
|
|
17718
17722
|
const sendsBody = body !== void 0 && (method === "POST" || method === "PUT" || method === "PATCH");
|
|
17719
17723
|
const response = await builderRequest(client, method, url, sendsBody ? { body } : {});
|
|
17720
17724
|
if (!response.ok) {
|
|
17721
17725
|
const text2 = await response.text();
|
|
17722
|
-
throw new Error(`Pipeline API Error ${response.status}: ${method} ${
|
|
17726
|
+
throw new Error(`Pipeline API Error ${response.status}: ${method} ${path40}
|
|
17723
17727
|
${text2}`);
|
|
17724
17728
|
}
|
|
17725
17729
|
const text = await response.text();
|
|
@@ -18586,9 +18590,9 @@ function planFromPayload(payload) {
|
|
|
18586
18590
|
return payload?.plan === "command-os" ? "command-os" : "mcp";
|
|
18587
18591
|
}
|
|
18588
18592
|
function legacyDeviceFingerprint() {
|
|
18589
|
-
const
|
|
18593
|
+
const os8 = require("node:os");
|
|
18590
18594
|
const crypto7 = require("node:crypto");
|
|
18591
|
-
const raw = `${
|
|
18595
|
+
const raw = `${os8.hostname()}:${os8.userInfo().username}:${os8.platform()}:${os8.arch()}`;
|
|
18592
18596
|
return crypto7.createHash("sha256").update(raw).digest("hex").slice(0, 16);
|
|
18593
18597
|
}
|
|
18594
18598
|
function deviceFingerprint(opts) {
|
|
@@ -23188,6 +23192,97 @@ var init_build_obligations = __esm({
|
|
|
23188
23192
|
}
|
|
23189
23193
|
});
|
|
23190
23194
|
|
|
23195
|
+
// src/claude-locate.ts
|
|
23196
|
+
function unusableShimMessage(shim) {
|
|
23197
|
+
return `Claude Code on this computer is an npm shim Command OS cannot start safely (${shim}). Install Claude Code with Anthropic's installer, then reopen the cockpit.`;
|
|
23198
|
+
}
|
|
23199
|
+
function runnable(p) {
|
|
23200
|
+
try {
|
|
23201
|
+
fs14.accessSync(p, fs14.constants.X_OK);
|
|
23202
|
+
return true;
|
|
23203
|
+
} catch {
|
|
23204
|
+
return false;
|
|
23205
|
+
}
|
|
23206
|
+
}
|
|
23207
|
+
function claudeFileNames(platform2, env) {
|
|
23208
|
+
if (platform2 !== "win32") return ["claude"];
|
|
23209
|
+
const exts = (env.PATHEXT || ".COM;.EXE;.BAT;.CMD").split(";").map((e) => e.trim().toLowerCase()).filter(Boolean);
|
|
23210
|
+
return [...exts.map((ext) => `claude${ext}`), "claude"];
|
|
23211
|
+
}
|
|
23212
|
+
function isShim(file2) {
|
|
23213
|
+
const ext = path13.extname(file2).toLowerCase();
|
|
23214
|
+
return ext === ".cmd" || ext === ".bat";
|
|
23215
|
+
}
|
|
23216
|
+
function shimTarget(shim) {
|
|
23217
|
+
let text;
|
|
23218
|
+
try {
|
|
23219
|
+
text = fs14.readFileSync(shim, "utf8");
|
|
23220
|
+
} catch {
|
|
23221
|
+
return null;
|
|
23222
|
+
}
|
|
23223
|
+
const refs = [...text.matchAll(/"%~?dp0%?\\([^"\r\n]+)"/g)].map((m) => m[1]);
|
|
23224
|
+
const resolve12 = (rel) => path13.join(path13.dirname(shim), ...rel.split(/[\\/]/));
|
|
23225
|
+
const script = refs.find((r) => /\.(?:js|cjs|mjs)$/i.test(r));
|
|
23226
|
+
if (script) {
|
|
23227
|
+
const p = resolve12(script);
|
|
23228
|
+
return fs14.existsSync(p) ? { kind: "script", path: p } : null;
|
|
23229
|
+
}
|
|
23230
|
+
const program = refs.find((r) => /\.exe$/i.test(r) && !/(^|[\\/])node\.exe$/i.test(r));
|
|
23231
|
+
if (program) {
|
|
23232
|
+
const p = resolve12(program);
|
|
23233
|
+
return fs14.existsSync(p) ? { kind: "program", path: p } : null;
|
|
23234
|
+
}
|
|
23235
|
+
return null;
|
|
23236
|
+
}
|
|
23237
|
+
function claudeBin(lookup = {}) {
|
|
23238
|
+
const platform2 = lookup.platform ?? process.platform;
|
|
23239
|
+
const env = lookup.env ?? process.env;
|
|
23240
|
+
const home = lookup.home ?? os3.homedir();
|
|
23241
|
+
const names = claudeFileNames(platform2, env);
|
|
23242
|
+
const dirs = (env.PATH || env.Path || "").split(platform2 === "win32" ? ";" : ":").map((d) => platform2 === "win32" ? d.replace(/^"|"$/g, "") : d).filter(Boolean);
|
|
23243
|
+
let unusableShim = null;
|
|
23244
|
+
for (const dir of dirs) {
|
|
23245
|
+
for (const name of names) {
|
|
23246
|
+
const candidate = path13.join(dir, name);
|
|
23247
|
+
if (!runnable(candidate)) continue;
|
|
23248
|
+
if (platform2 === "win32" && isShim(candidate) && !shimTarget(candidate)) {
|
|
23249
|
+
unusableShim ??= candidate;
|
|
23250
|
+
continue;
|
|
23251
|
+
}
|
|
23252
|
+
return candidate;
|
|
23253
|
+
}
|
|
23254
|
+
}
|
|
23255
|
+
const fallback = path13.join(home, ".local", "bin", platform2 === "win32" ? "claude.exe" : "claude");
|
|
23256
|
+
if (runnable(fallback)) return fallback;
|
|
23257
|
+
throw new Error(unusableShim ? unusableShimMessage(unusableShim) : NOT_FOUND_MESSAGE);
|
|
23258
|
+
}
|
|
23259
|
+
function claudeCommand(bin, args, lookup = {}) {
|
|
23260
|
+
const platform2 = lookup.platform ?? process.platform;
|
|
23261
|
+
if (platform2 !== "win32") return { command: bin, args };
|
|
23262
|
+
let resolved2 = bin;
|
|
23263
|
+
if (bin === "claude") {
|
|
23264
|
+
try {
|
|
23265
|
+
resolved2 = claudeBin(lookup);
|
|
23266
|
+
} catch {
|
|
23267
|
+
return { command: bin, args };
|
|
23268
|
+
}
|
|
23269
|
+
}
|
|
23270
|
+
if (!isShim(resolved2)) return { command: resolved2, args };
|
|
23271
|
+
const target = shimTarget(resolved2);
|
|
23272
|
+
if (!target) return { command: resolved2, args };
|
|
23273
|
+
return target.kind === "script" ? { command: lookup.execPath ?? process.execPath, args: [target.path, ...args] } : { command: target.path, args };
|
|
23274
|
+
}
|
|
23275
|
+
var fs14, os3, path13, NOT_FOUND_MESSAGE;
|
|
23276
|
+
var init_claude_locate = __esm({
|
|
23277
|
+
"src/claude-locate.ts"() {
|
|
23278
|
+
"use strict";
|
|
23279
|
+
fs14 = __toESM(require("node:fs"));
|
|
23280
|
+
os3 = __toESM(require("node:os"));
|
|
23281
|
+
path13 = __toESM(require("node:path"));
|
|
23282
|
+
NOT_FOUND_MESSAGE = "Claude Code CLI not found. Install it, then reopen the cockpit.";
|
|
23283
|
+
}
|
|
23284
|
+
});
|
|
23285
|
+
|
|
23191
23286
|
// src/claude-cli.ts
|
|
23192
23287
|
function parseVersion(text) {
|
|
23193
23288
|
const m = /(\d+)\.(\d+)\.(\d+)/.exec(text || "");
|
|
@@ -23205,7 +23300,8 @@ function meetsFloor(version, floor = MIN_CLAUDE_VERSION) {
|
|
|
23205
23300
|
}
|
|
23206
23301
|
function readClaudeVersion(bin) {
|
|
23207
23302
|
try {
|
|
23208
|
-
const
|
|
23303
|
+
const cmd = claudeCommand(bin, ["--version"]);
|
|
23304
|
+
const r = (0, import_child_process2.spawnSync)(cmd.command, cmd.args, { encoding: "utf8", timeout: 15e3 });
|
|
23209
23305
|
if (r.error || r.status !== 0) return null;
|
|
23210
23306
|
const parsed2 = parseVersion(`${r.stdout ?? ""} ${r.stderr ?? ""}`);
|
|
23211
23307
|
return parsed2 ? `${parsed2.major}.${parsed2.minor}.${parsed2.patch}` : null;
|
|
@@ -23218,21 +23314,21 @@ function tooOldMessage(found) {
|
|
|
23218
23314
|
return `Command OS needs Claude Code ${MIN_CLAUDE_VERSION} or newer to run a stage. ${seen} Update Claude Code, then reopen Command OS. Nothing was started and nothing was spent.`;
|
|
23219
23315
|
}
|
|
23220
23316
|
function enginePath() {
|
|
23221
|
-
return
|
|
23317
|
+
return path14.join(appDataDir(), "command-os-engine.json");
|
|
23222
23318
|
}
|
|
23223
23319
|
function readEngineRecord(file2 = enginePath()) {
|
|
23224
23320
|
try {
|
|
23225
|
-
const r = JSON.parse(
|
|
23321
|
+
const r = JSON.parse(fs15.readFileSync(file2, "utf8"));
|
|
23226
23322
|
return typeof r?.version === "string" ? r : null;
|
|
23227
23323
|
} catch {
|
|
23228
23324
|
return null;
|
|
23229
23325
|
}
|
|
23230
23326
|
}
|
|
23231
23327
|
function writeEngineRecord(rec, file2 = enginePath()) {
|
|
23232
|
-
|
|
23233
|
-
|
|
23328
|
+
fs15.mkdirSync(path14.dirname(file2), { recursive: true });
|
|
23329
|
+
fs15.writeFileSync(file2, JSON.stringify(rec, null, 2), { mode: 384 });
|
|
23234
23330
|
try {
|
|
23235
|
-
|
|
23331
|
+
fs15.chmodSync(file2, 384);
|
|
23236
23332
|
} catch {
|
|
23237
23333
|
}
|
|
23238
23334
|
}
|
|
@@ -23263,7 +23359,7 @@ function runSmoke(bin, timeoutMs = 9e4) {
|
|
|
23263
23359
|
resolve12(r);
|
|
23264
23360
|
}
|
|
23265
23361
|
};
|
|
23266
|
-
const
|
|
23362
|
+
const cmd = claudeCommand(bin, [
|
|
23267
23363
|
"-p",
|
|
23268
23364
|
SMOKE_PROMPT,
|
|
23269
23365
|
"--output-format",
|
|
@@ -23276,7 +23372,8 @@ function runSmoke(bin, timeoutMs = 9e4) {
|
|
|
23276
23372
|
// model needs no tool to answer with one line.
|
|
23277
23373
|
"--disallowedTools",
|
|
23278
23374
|
"Bash,Read,Write,Edit,Glob,Grep,WebFetch,WebSearch,Task"
|
|
23279
|
-
]
|
|
23375
|
+
]);
|
|
23376
|
+
const child = (0, import_child_process2.spawn)(cmd.command, cmd.args, { stdio: ["ignore", "pipe", "pipe"] });
|
|
23280
23377
|
const timer = setTimeout(() => {
|
|
23281
23378
|
child.kill("SIGKILL");
|
|
23282
23379
|
done({ ok: false, error: "the engine check did not finish in time" });
|
|
@@ -23370,14 +23467,15 @@ async function ensureEngineReady(bin, onNote = () => {
|
|
|
23370
23467
|
onNote(`Claude Code ${version} checked out${smoke.model ? ` (${smoke.model})` : ""}. Starting the step.`);
|
|
23371
23468
|
return { ok: true, version, smoked: true, smokeCostUsd: smoke.costUsd, smokeModel: smoke.model };
|
|
23372
23469
|
}
|
|
23373
|
-
var import_child_process2,
|
|
23470
|
+
var import_child_process2, fs15, path14, MIN_CLAUDE_VERSION, SMOKE_PROMPT;
|
|
23374
23471
|
var init_claude_cli = __esm({
|
|
23375
23472
|
"src/claude-cli.ts"() {
|
|
23376
23473
|
"use strict";
|
|
23377
23474
|
import_child_process2 = require("child_process");
|
|
23378
|
-
|
|
23379
|
-
|
|
23475
|
+
fs15 = __toESM(require("fs"));
|
|
23476
|
+
path14 = __toESM(require("path"));
|
|
23380
23477
|
init_credentials_store();
|
|
23478
|
+
init_claude_locate();
|
|
23381
23479
|
MIN_CLAUDE_VERSION = "2.1.0";
|
|
23382
23480
|
SMOKE_PROMPT = 'Reply with exactly this one line and nothing else: RESULT: {"ok":true,"summary":"engine check"}';
|
|
23383
23481
|
}
|
|
@@ -23545,17 +23643,17 @@ var init_stage_boundary = __esm({
|
|
|
23545
23643
|
// src/plan-guide.ts
|
|
23546
23644
|
function skillRoot() {
|
|
23547
23645
|
const candidates = [
|
|
23548
|
-
|
|
23646
|
+
path15.join(__dirname, "..", "skills", "blueprint"),
|
|
23549
23647
|
// dist/ → package root
|
|
23550
|
-
|
|
23648
|
+
path15.join(__dirname, "..", "..", "skills", "blueprint"),
|
|
23551
23649
|
// src/ in dev
|
|
23552
|
-
|
|
23650
|
+
path15.join(process.cwd(), "skills", "blueprint")
|
|
23553
23651
|
];
|
|
23554
|
-
return candidates.find((c) =>
|
|
23652
|
+
return candidates.find((c) => fs16.existsSync(path15.join(c, "references", "build-plan-schema.md"))) ?? null;
|
|
23555
23653
|
}
|
|
23556
23654
|
function readIf(p, max) {
|
|
23557
23655
|
try {
|
|
23558
|
-
const t =
|
|
23656
|
+
const t = fs16.readFileSync(p, "utf8");
|
|
23559
23657
|
return t.length > max ? t.slice(0, max) : t;
|
|
23560
23658
|
} catch {
|
|
23561
23659
|
return null;
|
|
@@ -23568,12 +23666,12 @@ function presetForIndustry(industry) {
|
|
|
23568
23666
|
function loadPlanGuide(industry) {
|
|
23569
23667
|
const root = skillRoot();
|
|
23570
23668
|
if (!root) return "";
|
|
23571
|
-
const schema = readIf(
|
|
23669
|
+
const schema = readIf(path15.join(root, "references", "build-plan-schema.md"), CAP_SCHEMA);
|
|
23572
23670
|
const presetName = presetForIndustry(industry);
|
|
23573
|
-
const preset = readIf(
|
|
23671
|
+
const preset = readIf(path15.join(root, "presets", `${presetName}.preset.json`), CAP_PRESET);
|
|
23574
23672
|
const exampleFile = EXAMPLE_FOR_PRESET[presetName] ?? "sample-build-plan.json";
|
|
23575
|
-
const example = readIf(
|
|
23576
|
-
const copyGuide = readIf(
|
|
23673
|
+
const example = readIf(path15.join(root, "examples", exampleFile), CAP_EXAMPLE);
|
|
23674
|
+
const copyGuide = readIf(path15.join(root, "references", "copy-guide.md"), CAP_COPY_GUIDE);
|
|
23577
23675
|
if (!schema) return "";
|
|
23578
23676
|
return `
|
|
23579
23677
|
PLAN GUIDE \u2014 everything you need to write the build plan is below. Do NOT try to read files; you cannot.
|
|
@@ -23591,12 +23689,12 @@ ${example}
|
|
|
23591
23689
|
` : "") + `=== End of plan guide ===
|
|
23592
23690
|
`;
|
|
23593
23691
|
}
|
|
23594
|
-
var
|
|
23692
|
+
var fs16, path15, PRESET_FOR_INDUSTRY, EXAMPLE_FOR_PRESET, COPY_INSTRUCTION, CALENDAR_INSTRUCTION, CAP_SCHEMA, CAP_PRESET, CAP_EXAMPLE, CAP_COPY_GUIDE;
|
|
23595
23693
|
var init_plan_guide = __esm({
|
|
23596
23694
|
"src/plan-guide.ts"() {
|
|
23597
23695
|
"use strict";
|
|
23598
|
-
|
|
23599
|
-
|
|
23696
|
+
fs16 = __toESM(require("fs"));
|
|
23697
|
+
path15 = __toESM(require("path"));
|
|
23600
23698
|
PRESET_FOR_INDUSTRY = {
|
|
23601
23699
|
"med-spa": "med-spa",
|
|
23602
23700
|
clinic: "clinic",
|
|
@@ -26210,10 +26308,10 @@ function progressLine(e) {
|
|
|
26210
26308
|
}
|
|
26211
26309
|
}
|
|
26212
26310
|
function defaultProgressDir(base = appDataDir()) {
|
|
26213
|
-
return
|
|
26311
|
+
return path16.join(base, "progress");
|
|
26214
26312
|
}
|
|
26215
26313
|
function progressFilePath(dir, locationId2) {
|
|
26216
|
-
return
|
|
26314
|
+
return path16.join(dir, `${locationId2.replace(/[^A-Za-z0-9_-]/g, "_")}.json`);
|
|
26217
26315
|
}
|
|
26218
26316
|
function createProgressFileSink(locationId2, plan, dir = process.env[PROGRESS_DIR_ENV]) {
|
|
26219
26317
|
if (!dir) return null;
|
|
@@ -26221,10 +26319,10 @@ function createProgressFileSink(locationId2, plan, dir = process.env[PROGRESS_DI
|
|
|
26221
26319
|
const file2 = progressFilePath(dir, locationId2);
|
|
26222
26320
|
const write = () => {
|
|
26223
26321
|
try {
|
|
26224
|
-
|
|
26322
|
+
fs17.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
26225
26323
|
const tmp = `${file2}.tmp`;
|
|
26226
|
-
|
|
26227
|
-
|
|
26324
|
+
fs17.writeFileSync(tmp, JSON.stringify({ locationId: locationId2, progress: tracker.snapshot() }), { mode: 384 });
|
|
26325
|
+
fs17.renameSync(tmp, file2);
|
|
26228
26326
|
} catch {
|
|
26229
26327
|
}
|
|
26230
26328
|
};
|
|
@@ -26242,7 +26340,7 @@ function createProgressFileSink(locationId2, plan, dir = process.env[PROGRESS_DI
|
|
|
26242
26340
|
}
|
|
26243
26341
|
function readProgressSnapshot(dir, locationId2) {
|
|
26244
26342
|
try {
|
|
26245
|
-
const raw = JSON.parse(
|
|
26343
|
+
const raw = JSON.parse(fs17.readFileSync(progressFilePath(dir, locationId2), "utf8"));
|
|
26246
26344
|
const p = raw?.progress;
|
|
26247
26345
|
if (!p || !Array.isArray(p.groups) || typeof p.updatedAt !== "string") return null;
|
|
26248
26346
|
return p;
|
|
@@ -26263,12 +26361,12 @@ function tapPush(arr, onItem) {
|
|
|
26263
26361
|
};
|
|
26264
26362
|
Object.defineProperty(arr, "push", { value: tapped, writable: true, configurable: true, enumerable: false });
|
|
26265
26363
|
}
|
|
26266
|
-
var
|
|
26364
|
+
var fs17, path16, PROGRESS_GROUPS, GROUP_INDEX, TYPE_TO_GROUP, HALT_TYPE_TO_GROUP, CREATING_LINE, BuildProgressTracker, PROGRESS_DIR_ENV;
|
|
26267
26365
|
var init_build_progress = __esm({
|
|
26268
26366
|
"src/build-progress.ts"() {
|
|
26269
26367
|
"use strict";
|
|
26270
|
-
|
|
26271
|
-
|
|
26368
|
+
fs17 = __toESM(require("fs"));
|
|
26369
|
+
path16 = __toESM(require("path"));
|
|
26272
26370
|
init_executor();
|
|
26273
26371
|
init_credentials_store();
|
|
26274
26372
|
PROGRESS_GROUPS = [
|
|
@@ -26406,10 +26504,10 @@ var init_build_progress = __esm({
|
|
|
26406
26504
|
|
|
26407
26505
|
// src/build-journal.ts
|
|
26408
26506
|
function journalDir(base = appDataDir()) {
|
|
26409
|
-
return
|
|
26507
|
+
return path17.join(base, "build-journal");
|
|
26410
26508
|
}
|
|
26411
26509
|
function journalPath(locationId2, base) {
|
|
26412
|
-
return
|
|
26510
|
+
return path17.join(journalDir(base), `${locationId2}.jsonl`);
|
|
26413
26511
|
}
|
|
26414
26512
|
function newRunId(now = /* @__PURE__ */ new Date(), rand = defaultRand) {
|
|
26415
26513
|
const iso2 = now.toISOString();
|
|
@@ -26469,16 +26567,16 @@ function buildRunRecord(input) {
|
|
|
26469
26567
|
}
|
|
26470
26568
|
function appendJournal(record, base) {
|
|
26471
26569
|
const dir = journalDir(base);
|
|
26472
|
-
|
|
26570
|
+
fs18.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
26473
26571
|
const file2 = journalPath(record.locationId, base);
|
|
26474
|
-
|
|
26572
|
+
fs18.appendFileSync(file2, `${JSON.stringify(record)}
|
|
26475
26573
|
`, { mode: 384 });
|
|
26476
26574
|
return file2;
|
|
26477
26575
|
}
|
|
26478
26576
|
function readJournal(locationId2, base) {
|
|
26479
26577
|
let raw;
|
|
26480
26578
|
try {
|
|
26481
|
-
raw =
|
|
26579
|
+
raw = fs18.readFileSync(journalPath(locationId2, base), "utf8");
|
|
26482
26580
|
} catch {
|
|
26483
26581
|
return [];
|
|
26484
26582
|
}
|
|
@@ -26522,12 +26620,12 @@ function describeRuns(locationId2, base) {
|
|
|
26522
26620
|
reverted: reverted.has(r.runId)
|
|
26523
26621
|
})).reverse();
|
|
26524
26622
|
}
|
|
26525
|
-
var
|
|
26623
|
+
var fs18, path17;
|
|
26526
26624
|
var init_build_journal = __esm({
|
|
26527
26625
|
"src/build-journal.ts"() {
|
|
26528
26626
|
"use strict";
|
|
26529
|
-
|
|
26530
|
-
|
|
26627
|
+
fs18 = __toESM(require("fs"));
|
|
26628
|
+
path17 = __toESM(require("path"));
|
|
26531
26629
|
init_credentials_store();
|
|
26532
26630
|
}
|
|
26533
26631
|
});
|
|
@@ -26578,24 +26676,6 @@ function stageBlockedBy(stages, stage) {
|
|
|
26578
26676
|
}
|
|
26579
26677
|
return null;
|
|
26580
26678
|
}
|
|
26581
|
-
function claudeBin() {
|
|
26582
|
-
const fallback = path17.join(os3.homedir(), ".local", "bin", "claude");
|
|
26583
|
-
const fromPath = (process.env.PATH || "").split(path17.delimiter).map((d) => path17.join(d, "claude")).find((p) => {
|
|
26584
|
-
try {
|
|
26585
|
-
fs18.accessSync(p, fs18.constants.X_OK);
|
|
26586
|
-
return true;
|
|
26587
|
-
} catch {
|
|
26588
|
-
return false;
|
|
26589
|
-
}
|
|
26590
|
-
});
|
|
26591
|
-
if (fromPath) return fromPath;
|
|
26592
|
-
try {
|
|
26593
|
-
fs18.accessSync(fallback, fs18.constants.X_OK);
|
|
26594
|
-
return fallback;
|
|
26595
|
-
} catch {
|
|
26596
|
-
}
|
|
26597
|
-
throw new Error("Claude Code CLI not found. Install it, then reopen the cockpit.");
|
|
26598
|
-
}
|
|
26599
26679
|
function verifyReadbackPrompt(ctx) {
|
|
26600
26680
|
return "\nAccount read-back has already run in code. These records decide existence, regardless of RESULT ok or dependencyChecks: " + JSON.stringify(ctx?.readback ?? []) + "\nDo not contradict any record. Keep dependencyChecks for evidence you establish yourself. The final report replaces the standing Checked and Not checked headings with the deterministic roll-call. Use the live URL on the funnel or page record, and the triggerTags and id on the speed-to-lead workflow record, for the existing fake-client check. Do not invent a URL or a trigger tag. Before calling verify_funnel, disclose that it submits a marked test lead and may fire this client's automations once. Preserve the existing verify_funnel submission and cleanup. If the funnel has no working page, report the reason that live proof could not run. Add liveProof:{ok,evidence} to RESULT. Set liveProof.ok true only when verify_funnel reports that the marked lead arrived and the test contact was deleted. Quote that result's evidence, including its contact id and cleanup result. Otherwise set liveProof.ok false with the check's own failure or the reason it could not run.\n";
|
|
26601
26681
|
}
|
|
@@ -26662,11 +26742,12 @@ function runStage(locationId2, stage, onEvent, opts = {}) {
|
|
|
26662
26742
|
const progressDir = opts.onProgress ? defaultProgressDir() : void 0;
|
|
26663
26743
|
if (progressDir) {
|
|
26664
26744
|
try {
|
|
26665
|
-
|
|
26745
|
+
fs19.rmSync(progressFilePath(progressDir, locationId2), { force: true });
|
|
26666
26746
|
} catch {
|
|
26667
26747
|
}
|
|
26668
26748
|
}
|
|
26669
|
-
const
|
|
26749
|
+
const cmd = claudeCommand(bin, buildClaudeArgs(spec, locationId2, locationName, opts.context));
|
|
26750
|
+
const child = (0, import_child_process3.spawn)(cmd.command, cmd.args, {
|
|
26670
26751
|
stdio: ["ignore", "pipe", "pipe"],
|
|
26671
26752
|
env: { ...process.env, ...progressDir ? { [PROGRESS_DIR_ENV]: progressDir } : {}, GHL_COMMAND_STAGE: String(stage), GHL_COMMAND_INTAKE_HASH: opts.context?.inputHash ?? "" }
|
|
26672
26753
|
});
|
|
@@ -26775,21 +26856,20 @@ function runStage(locationId2, stage, onEvent, opts = {}) {
|
|
|
26775
26856
|
});
|
|
26776
26857
|
});
|
|
26777
26858
|
}
|
|
26778
|
-
var import_child_process3,
|
|
26859
|
+
var import_child_process3, fs19, STAFF_NOTIFICATION_RULE, MISSING_STEP_RULE, NO_INVENTED_CAUSE_RULE, STAGE_SPECS, HUMAN_GATE_STAGES;
|
|
26779
26860
|
var init_stage_runner = __esm({
|
|
26780
26861
|
"src/stage-runner.ts"() {
|
|
26781
26862
|
"use strict";
|
|
26782
26863
|
init_intake_notification();
|
|
26783
26864
|
import_child_process3 = require("child_process");
|
|
26784
|
-
|
|
26785
|
-
os3 = __toESM(require("os"));
|
|
26786
|
-
path17 = __toESM(require("path"));
|
|
26865
|
+
fs19 = __toESM(require("fs"));
|
|
26787
26866
|
init_stage_boundary();
|
|
26788
26867
|
init_plan_guide();
|
|
26789
26868
|
init_question_set();
|
|
26790
26869
|
init_build_progress();
|
|
26791
26870
|
init_build_journal();
|
|
26792
26871
|
init_claude_cli();
|
|
26872
|
+
init_claude_locate();
|
|
26793
26873
|
init_account_rules();
|
|
26794
26874
|
init_account_rules();
|
|
26795
26875
|
STAFF_NOTIFICATION_RULE = `Staff notifications ALWAYS stay in the workflow. Call get_users on THIS account and use only a user id it returns as the recipient. If get_users returns no users, leave the recipient EMPTY ("") \u2014 never use a user id from another account, from the agency, from an earlier conversation or from memory (a foreign id looks like it worked and notifies nobody). An empty recipient can make publish fail: then keep the workflow as a draft with the notification step still in it, and list that step in the RESULT issues as "waiting for a staff member: add one in Settings \u2192 My Staff, then pick them on the notification step and publish".`;
|
|
@@ -27254,17 +27334,17 @@ function runCostPath() {
|
|
|
27254
27334
|
return path18.join(appDataDir(), "command-os-run-cost.jsonl");
|
|
27255
27335
|
}
|
|
27256
27336
|
function recordRunCost(rec, file2 = runCostPath()) {
|
|
27257
|
-
|
|
27258
|
-
|
|
27337
|
+
fs20.mkdirSync(path18.dirname(file2), { recursive: true });
|
|
27338
|
+
fs20.appendFileSync(file2, `${JSON.stringify(rec)}
|
|
27259
27339
|
`, { mode: 384 });
|
|
27260
27340
|
try {
|
|
27261
|
-
|
|
27341
|
+
fs20.chmodSync(file2, 384);
|
|
27262
27342
|
} catch {
|
|
27263
27343
|
}
|
|
27264
27344
|
}
|
|
27265
27345
|
function readRunCosts(file2 = runCostPath()) {
|
|
27266
27346
|
try {
|
|
27267
|
-
return
|
|
27347
|
+
return fs20.readFileSync(file2, "utf8").split("\n").filter((l) => l.trim()).map((l) => {
|
|
27268
27348
|
try {
|
|
27269
27349
|
return JSON.parse(l);
|
|
27270
27350
|
} catch {
|
|
@@ -27295,11 +27375,11 @@ function costLines(o) {
|
|
|
27295
27375
|
if (o.engineCheckRan && o.engineCheckCostUsd === void 0) out.push("Claude readiness check: cost not reported.");
|
|
27296
27376
|
return out;
|
|
27297
27377
|
}
|
|
27298
|
-
var
|
|
27378
|
+
var fs20, path18;
|
|
27299
27379
|
var init_run_cost = __esm({
|
|
27300
27380
|
"src/run-cost.ts"() {
|
|
27301
27381
|
"use strict";
|
|
27302
|
-
|
|
27382
|
+
fs20 = __toESM(require("fs"));
|
|
27303
27383
|
path18 = __toESM(require("path"));
|
|
27304
27384
|
init_credentials_store();
|
|
27305
27385
|
}
|
|
@@ -27314,7 +27394,8 @@ function callClaudeRecorded(bin, argv, context, onCost = () => {
|
|
|
27314
27394
|
let stderr = "";
|
|
27315
27395
|
let facts = {};
|
|
27316
27396
|
let settled = false;
|
|
27317
|
-
const
|
|
27397
|
+
const cmd = claudeCommand(bin, [...argv, "--output-format", "stream-json", "--verbose"]);
|
|
27398
|
+
const child = (0, import_node_child_process2.spawn)(cmd.command, cmd.args, { stdio: ["ignore", "pipe", "pipe"] });
|
|
27318
27399
|
const timer = setTimeout(() => child.kill("SIGKILL"), 6 * 60 * 1e3);
|
|
27319
27400
|
const finish3 = (ok) => {
|
|
27320
27401
|
if (settled) return;
|
|
@@ -27365,6 +27446,7 @@ var init_claude_call = __esm({
|
|
|
27365
27446
|
import_node_child_process2 = require("node:child_process");
|
|
27366
27447
|
init_run_cost();
|
|
27367
27448
|
init_claude_cli();
|
|
27449
|
+
init_claude_locate();
|
|
27368
27450
|
}
|
|
27369
27451
|
});
|
|
27370
27452
|
|
|
@@ -28127,14 +28209,14 @@ function plansDir(base = appDataDir()) {
|
|
|
28127
28209
|
}
|
|
28128
28210
|
function savePlanRecord(rec, base) {
|
|
28129
28211
|
const dir = plansDir(base);
|
|
28130
|
-
|
|
28212
|
+
fs21.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
28131
28213
|
const file2 = path19.join(dir, `${rec.locationId}.json`);
|
|
28132
|
-
|
|
28214
|
+
fs21.writeFileSync(file2, JSON.stringify({ ...rec, contentHash: contentHash(rec.plan), inputHash: rec.inputHash ?? process.env.GHL_COMMAND_INTAKE_HASH, dependencies: rec.dependencies ?? planDependencies(rec.plan) }, null, 2), { mode: 384 });
|
|
28133
28215
|
return file2;
|
|
28134
28216
|
}
|
|
28135
28217
|
function readSavedPlan(locationId2, base) {
|
|
28136
28218
|
try {
|
|
28137
|
-
return JSON.parse(
|
|
28219
|
+
return JSON.parse(fs21.readFileSync(path19.join(plansDir(base), `${locationId2}.json`), "utf8"));
|
|
28138
28220
|
} catch {
|
|
28139
28221
|
return null;
|
|
28140
28222
|
}
|
|
@@ -28157,11 +28239,11 @@ function planSummary(rec) {
|
|
|
28157
28239
|
if (wfs.length) parts.push(`workflows: ${wfs.map((w) => `${w.name} (${Array.isArray(w.actions) ? w.actions.length : "?"} actions)`).join("; ")}`);
|
|
28158
28240
|
return parts.join("\n") || "(plan has no recognisable sections)";
|
|
28159
28241
|
}
|
|
28160
|
-
var
|
|
28242
|
+
var fs21, path19;
|
|
28161
28243
|
var init_plan_store = __esm({
|
|
28162
28244
|
"src/plan-store.ts"() {
|
|
28163
28245
|
"use strict";
|
|
28164
|
-
|
|
28246
|
+
fs21 = __toESM(require("fs"));
|
|
28165
28247
|
path19 = __toESM(require("path"));
|
|
28166
28248
|
init_credentials_store();
|
|
28167
28249
|
init_build_contract();
|
|
@@ -28179,7 +28261,8 @@ function askNoTools(prompt, opts = {}) {
|
|
|
28179
28261
|
resolve12("");
|
|
28180
28262
|
return;
|
|
28181
28263
|
}
|
|
28182
|
-
const
|
|
28264
|
+
const cmd = claudeCommand(bin, ["-p", prompt, "--output-format", "stream-json", "--verbose", "--max-turns", String(opts.maxTurns ?? 3), "--allowedTools", "", "--disallowedTools", NO_TOOLS_DENY]);
|
|
28265
|
+
const child = (0, import_node_child_process3.spawn)(cmd.command, cmd.args, { stdio: ["ignore", "pipe", "pipe"], env: { ...process.env } });
|
|
28183
28266
|
const timeout = setTimeout(() => child.kill("SIGKILL"), opts.timeoutMs ?? 3 * 60 * 1e3);
|
|
28184
28267
|
let lastText = "";
|
|
28185
28268
|
let buffered = "";
|
|
@@ -28228,6 +28311,7 @@ var init_handoff_run = __esm({
|
|
|
28228
28311
|
"use strict";
|
|
28229
28312
|
import_node_child_process3 = require("node:child_process");
|
|
28230
28313
|
init_stage_runner();
|
|
28314
|
+
init_claude_locate();
|
|
28231
28315
|
init_handoff_pack();
|
|
28232
28316
|
CLIENT_SENTENCE_RULES = "Write for the business owner, not a developer. Complete sentences that end with a period; never trail off. One or two sentences per item, no more. Everyday words: say 'text messaging registration (A2P)' not 'A2P'; 'the calendar is not connected to Google or Outlook yet' not 'OAuth'; 'nothing was wrong' not 'status ok'. No tool names, no ids, no field names in code style, no JSON, no arrows. Keep the names of workflows, pipelines and forms because the client will recognise them. If a list is long, name the first few and say how many in total. State what is in place, or what is missing and why, plainly. An item marked 'done but not confirmed' has NOT been confirmed: never describe it as having happened or been observed; say what is still to be confirmed and how (for example, 'this is confirmed on the handoff call, when you complete one task in the account without help').";
|
|
28233
28317
|
NO_TOOLS_DENY = "Bash,Write,Edit,MultiEdit,NotebookEdit,WebFetch,WebSearch,Task,TodoWrite,Read,Glob,Grep";
|
|
@@ -28392,7 +28476,7 @@ var init_client_capabilities = __esm({
|
|
|
28392
28476
|
{ key: "reputation", label: "Reputation and content", blurb: "Reviews, social posts and the blog." },
|
|
28393
28477
|
{ key: "health", label: "Health and handoff", blurb: "Proving it works, keeping it clean, and handing it over." }
|
|
28394
28478
|
];
|
|
28395
|
-
N = (label,
|
|
28479
|
+
N = (label, path40, params, key, nameField, version) => ({ label, path: path40, params, key, nameField, version });
|
|
28396
28480
|
AI2 = "2021-04-15";
|
|
28397
28481
|
CAPABILITIES = [
|
|
28398
28482
|
// ── Set up ──────────────────────────────────────────────────────────────
|
|
@@ -28653,7 +28737,7 @@ var require_package = __commonJS({
|
|
|
28653
28737
|
"package.json"(exports2, module2) {
|
|
28654
28738
|
module2.exports = {
|
|
28655
28739
|
name: "@elitedcs/ghl-mcp",
|
|
28656
|
-
version: "3.83.
|
|
28740
|
+
version: "3.83.9",
|
|
28657
28741
|
mcpName: "io.github.drjerryrelth/ghl-command",
|
|
28658
28742
|
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.",
|
|
28659
28743
|
main: "dist/index.js",
|
|
@@ -28752,17 +28836,17 @@ var require_package = __commonJS({
|
|
|
28752
28836
|
});
|
|
28753
28837
|
|
|
28754
28838
|
// src/package-version.ts
|
|
28755
|
-
var
|
|
28839
|
+
var fs22, path20, bundledPkg, pkg;
|
|
28756
28840
|
var init_package_version = __esm({
|
|
28757
28841
|
"src/package-version.ts"() {
|
|
28758
28842
|
"use strict";
|
|
28759
|
-
|
|
28843
|
+
fs22 = __toESM(require("fs"));
|
|
28760
28844
|
path20 = __toESM(require("path"));
|
|
28761
28845
|
bundledPkg = require_package();
|
|
28762
28846
|
pkg = (() => {
|
|
28763
28847
|
try {
|
|
28764
28848
|
const onDisk = JSON.parse(
|
|
28765
|
-
|
|
28849
|
+
fs22.readFileSync(path20.resolve(__dirname, "..", "package.json"), "utf8")
|
|
28766
28850
|
);
|
|
28767
28851
|
if (typeof onDisk.version === "string" && onDisk.version.length > 0) {
|
|
28768
28852
|
return { version: onDisk.version };
|
|
@@ -29034,7 +29118,7 @@ function sanitizeEngagements(raw) {
|
|
|
29034
29118
|
}
|
|
29035
29119
|
function readClientEngagements(file2 = clientEngagementsPath()) {
|
|
29036
29120
|
try {
|
|
29037
|
-
const raw = JSON.parse(
|
|
29121
|
+
const raw = JSON.parse(fs23.readFileSync(file2, "utf8"));
|
|
29038
29122
|
if (raw && typeof raw === "object") return sanitizeEngagements(raw);
|
|
29039
29123
|
} catch {
|
|
29040
29124
|
}
|
|
@@ -29061,7 +29145,7 @@ function writeClientEngagement(locationId2, patch, file2 = clientEngagementsPath
|
|
|
29061
29145
|
if (!id || FORBIDDEN_KEY.has(id)) throw new Error("An id is required.");
|
|
29062
29146
|
const stamp = () => {
|
|
29063
29147
|
try {
|
|
29064
|
-
return
|
|
29148
|
+
return fs23.readFileSync(file2, "utf8");
|
|
29065
29149
|
} catch {
|
|
29066
29150
|
return "";
|
|
29067
29151
|
}
|
|
@@ -29081,20 +29165,20 @@ function writeClientEngagement(locationId2, patch, file2 = clientEngagementsPath
|
|
|
29081
29165
|
};
|
|
29082
29166
|
const persist = (next) => {
|
|
29083
29167
|
if (file2 === clientEngagementsPath() && !process.env[CLIENT_ENGAGEMENTS_ENV]) ensureAppDataDir();
|
|
29084
|
-
|
|
29168
|
+
fs23.mkdirSync(path21.dirname(file2), { recursive: true });
|
|
29085
29169
|
const tmp = `${file2}.tmp.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}`;
|
|
29086
29170
|
try {
|
|
29087
|
-
|
|
29171
|
+
fs23.writeFileSync(tmp, JSON.stringify(next, null, 2) + "\n", "utf-8");
|
|
29088
29172
|
if (process.platform !== "win32") {
|
|
29089
29173
|
try {
|
|
29090
|
-
|
|
29174
|
+
fs23.chmodSync(tmp, 384);
|
|
29091
29175
|
} catch {
|
|
29092
29176
|
}
|
|
29093
29177
|
}
|
|
29094
|
-
|
|
29178
|
+
fs23.renameSync(tmp, file2);
|
|
29095
29179
|
} catch (e) {
|
|
29096
29180
|
try {
|
|
29097
|
-
|
|
29181
|
+
fs23.unlinkSync(tmp);
|
|
29098
29182
|
} catch {
|
|
29099
29183
|
}
|
|
29100
29184
|
throw e;
|
|
@@ -29166,11 +29250,11 @@ function monthlyFloor(roster, profile) {
|
|
|
29166
29250
|
byKind
|
|
29167
29251
|
};
|
|
29168
29252
|
}
|
|
29169
|
-
var
|
|
29253
|
+
var fs23, path21, CLIENT_ENGAGEMENTS_ENV, ENGAGEMENT_STATUSES, ENGAGEMENT_KINDS, DEFAULT_ENGAGEMENT_KIND, EMPTY2, MAX_NOTES, MAX_SOP2, MAX_CLIENTS, FORBIDDEN_KEY;
|
|
29170
29254
|
var init_client_engagements = __esm({
|
|
29171
29255
|
"src/client-engagements.ts"() {
|
|
29172
29256
|
"use strict";
|
|
29173
|
-
|
|
29257
|
+
fs23 = __toESM(require("fs"));
|
|
29174
29258
|
path21 = __toESM(require("path"));
|
|
29175
29259
|
init_credentials_store();
|
|
29176
29260
|
init_field_sanitize();
|
|
@@ -29189,7 +29273,7 @@ var init_client_engagements = __esm({
|
|
|
29189
29273
|
// src/agency-profile-page.ts
|
|
29190
29274
|
function agencyProfilePageHtml(p, clients = [], seatToken = "") {
|
|
29191
29275
|
const offers = (p.offers ?? []).map((o) => `${o.name}${o.priceFloor ? ` - ${o.priceFloor}${o.cadence === "monthly" ? "/mo" : ""}` : ""}`).join("\n");
|
|
29192
|
-
const
|
|
29276
|
+
const os8 = p.agencyOs ?? {};
|
|
29193
29277
|
const lines = (xs) => (xs ?? []).join("\n");
|
|
29194
29278
|
const sops = (p.delivery?.sops ?? []).join("\n");
|
|
29195
29279
|
const staff = (p.staff ?? []).map((m) => [m.name, m.role, m.email].filter(Boolean).join(" \xB7 ")).join("\n");
|
|
@@ -29301,50 +29385,50 @@ ${cockpitHeaderHtml("agency")}
|
|
|
29301
29385
|
<fieldset><legend>Your brand: who you are</legend>
|
|
29302
29386
|
<p class="hint">Filled in once, in your own words. Used on the assessment report, in proposals, in every Claude session's opening brief, and by every Command OS build.</p>
|
|
29303
29387
|
<label for="story">Your story: why this agency exists, in two or three sentences</label>
|
|
29304
|
-
<textarea id="story" name="story" placeholder="I ran a clinic for twelve years and watched good practices lose patients to voicemail\u2026">${esc4(
|
|
29388
|
+
<textarea id="story" name="story" placeholder="I ran a clinic for twelve years and watched good practices lose patients to voicemail\u2026">${esc4(os8.story)}</textarea>
|
|
29305
29389
|
<label for="method">Your method: how you get results, and what you call it</label>
|
|
29306
|
-
<textarea id="method" name="method" placeholder="The Full Room method: one weekend, a full calendar, a system that keeps booking after we leave">${esc4(
|
|
29390
|
+
<textarea id="method" name="method" placeholder="The Full Room method: one weekend, a full calendar, a system that keeps booking after we leave">${esc4(os8.method)}</textarea>
|
|
29307
29391
|
<div class="two">
|
|
29308
|
-
<div><label for="voice">How you sound: three words, then a sentence</label><textarea id="voice" name="voice" placeholder="Direct, warm, clinical. Specifics over hype.">${esc4(
|
|
29309
|
-
<div><label for="goals">Your goal for the next twelve months</label><textarea id="goals" name="goals" placeholder="$50k a month from 51 subscribers">${esc4(
|
|
29392
|
+
<div><label for="voice">How you sound: three words, then a sentence</label><textarea id="voice" name="voice" placeholder="Direct, warm, clinical. Specifics over hype.">${esc4(os8.voice)}</textarea></div>
|
|
29393
|
+
<div><label for="goals">Your goal for the next twelve months</label><textarea id="goals" name="goals" placeholder="$50k a month from 51 subscribers">${esc4(os8.goals)}</textarea></div>
|
|
29310
29394
|
</div>
|
|
29311
29395
|
<label for="values">What you stand for and what you refuse to do: one per line</label>
|
|
29312
|
-
<textarea id="values" name="values" placeholder="Never a false all-clear No cold texting anyone who did not opt in">${esc4(lines(
|
|
29396
|
+
<textarea id="values" name="values" placeholder="Never a false all-clear No cold texting anyone who did not opt in">${esc4(lines(os8.values))}</textarea>
|
|
29313
29397
|
<label for="proof">Proof you may state, with permission: one per line</label>
|
|
29314
|
-
<textarea id="proof" name="proof" placeholder="Radiance Med Spa: 34 consults booked in one weekend (permission on file)">${esc4(lines(
|
|
29398
|
+
<textarea id="proof" name="proof" placeholder="Radiance Med Spa: 34 consults booked in one weekend (permission on file)">${esc4(lines(os8.proof))}</textarea>
|
|
29315
29399
|
</fieldset>
|
|
29316
29400
|
|
|
29317
29401
|
<fieldset><legend>Ideal client: who you serve</legend>
|
|
29318
29402
|
<label for="icaWho">Who they are: role, business, size, where</label>
|
|
29319
|
-
<textarea id="icaWho" name="icaWho" placeholder="The owner of a two-provider med spa in a mid-size US city, doing $40k a month and answering the phone herself">${esc4(
|
|
29403
|
+
<textarea id="icaWho" name="icaWho" placeholder="The owner of a two-provider med spa in a mid-size US city, doing $40k a month and answering the phone herself">${esc4(os8.ica?.who)}</textarea>
|
|
29320
29404
|
<div class="two">
|
|
29321
|
-
<div><label for="icaPains">Their pains, worst first: one per line</label><textarea id="icaPains" name="icaPains">${esc4(lines(
|
|
29322
|
-
<div><label for="icaDesires">What they want, most first: one per line</label><textarea id="icaDesires" name="icaDesires">${esc4(lines(
|
|
29405
|
+
<div><label for="icaPains">Their pains, worst first: one per line</label><textarea id="icaPains" name="icaPains">${esc4(lines(os8.ica?.pains))}</textarea></div>
|
|
29406
|
+
<div><label for="icaDesires">What they want, most first: one per line</label><textarea id="icaDesires" name="icaDesires">${esc4(lines(os8.ica?.desires))}</textarea></div>
|
|
29323
29407
|
</div>
|
|
29324
29408
|
<div class="two">
|
|
29325
|
-
<div><label for="icaObjections">Why they say no: one per line</label><textarea id="icaObjections" name="icaObjections">${esc4(lines(
|
|
29326
|
-
<div><label for="icaTriggers">What makes them buy now: one per line</label><textarea id="icaTriggers" name="icaTriggers">${esc4(lines(
|
|
29409
|
+
<div><label for="icaObjections">Why they say no: one per line</label><textarea id="icaObjections" name="icaObjections">${esc4(lines(os8.ica?.objections))}</textarea></div>
|
|
29410
|
+
<div><label for="icaTriggers">What makes them buy now: one per line</label><textarea id="icaTriggers" name="icaTriggers">${esc4(lines(os8.ica?.triggers))}</textarea></div>
|
|
29327
29411
|
</div>
|
|
29328
29412
|
<div class="two">
|
|
29329
|
-
<div><label for="icaVocabulary">Their own words for the problem: one per line</label><textarea id="icaVocabulary" name="icaVocabulary">${esc4(lines(
|
|
29330
|
-
<div><label for="icaHangouts">Where they read, listen and gather: one per line</label><textarea id="icaHangouts" name="icaHangouts">${esc4(lines(
|
|
29413
|
+
<div><label for="icaVocabulary">Their own words for the problem: one per line</label><textarea id="icaVocabulary" name="icaVocabulary">${esc4(lines(os8.ica?.vocabulary))}</textarea></div>
|
|
29414
|
+
<div><label for="icaHangouts">Where they read, listen and gather: one per line</label><textarea id="icaHangouts" name="icaHangouts">${esc4(lines(os8.ica?.hangouts))}</textarea></div>
|
|
29331
29415
|
</div>
|
|
29332
29416
|
<label for="icaVillains">What they have tried and blame: one per line</label>
|
|
29333
|
-
<textarea id="icaVillains" name="icaVillains" placeholder="The last agency that charged $3,000 a month for two posts a week">${esc4(lines(
|
|
29417
|
+
<textarea id="icaVillains" name="icaVillains" placeholder="The last agency that charged $3,000 a month for two posts a week">${esc4(lines(os8.ica?.villains))}</textarea>
|
|
29334
29418
|
</fieldset>
|
|
29335
29419
|
|
|
29336
29420
|
<fieldset><legend>The offer: what you promise</legend>
|
|
29337
29421
|
<div class="two">
|
|
29338
|
-
<div><label for="offerName">The offer's name</label><input id="offerName" name="offerName" value="${esc4(
|
|
29339
|
-
<div><label for="offerPrice">Price, and what it is per</label><input id="offerPrice" name="offerPrice" value="${esc4(
|
|
29422
|
+
<div><label for="offerName">The offer's name</label><input id="offerName" name="offerName" value="${esc4(os8.offer?.name)}" placeholder="The Full Room"></div>
|
|
29423
|
+
<div><label for="offerPrice">Price, and what it is per</label><input id="offerPrice" name="offerPrice" value="${esc4(os8.offer?.price)}" placeholder="$5,000 per event plus 30% of revenue"></div>
|
|
29340
29424
|
</div>
|
|
29341
29425
|
<label for="offerPromise">The promise: one sentence a prospect could repeat</label>
|
|
29342
|
-
<textarea id="offerPromise" name="offerPromise" placeholder="A full room in one weekend, and a system that keeps booking after we leave">${esc4(
|
|
29426
|
+
<textarea id="offerPromise" name="offerPromise" placeholder="A full room in one weekend, and a system that keeps booking after we leave">${esc4(os8.offer?.promise)}</textarea>
|
|
29343
29427
|
<label for="offerStack">What is in it: one per line</label>
|
|
29344
|
-
<textarea id="offerStack" name="offerStack" placeholder="The campaign page The AI receptionist The 30-day nurture">${esc4(lines(
|
|
29428
|
+
<textarea id="offerStack" name="offerStack" placeholder="The campaign page The AI receptionist The 30-day nurture">${esc4(lines(os8.offer?.valueStack))}</textarea>
|
|
29345
29429
|
<div class="two">
|
|
29346
|
-
<div><label for="offerGuarantee">The guarantee, exactly as you would say it</label><textarea id="offerGuarantee" name="offerGuarantee">${esc4(
|
|
29347
|
-
<div><label for="offerAccelerators">Why now: bonuses, deadlines, scarcity, one per line</label><textarea id="offerAccelerators" name="offerAccelerators">${esc4(lines(
|
|
29430
|
+
<div><label for="offerGuarantee">The guarantee, exactly as you would say it</label><textarea id="offerGuarantee" name="offerGuarantee">${esc4(os8.offer?.guarantee)}</textarea></div>
|
|
29431
|
+
<div><label for="offerAccelerators">Why now: bonuses, deadlines, scarcity, one per line</label><textarea id="offerAccelerators" name="offerAccelerators">${esc4(lines(os8.offer?.accelerators))}</textarea></div>
|
|
29348
29432
|
</div>
|
|
29349
29433
|
</fieldset>
|
|
29350
29434
|
|
|
@@ -31551,7 +31635,7 @@ function planLauncher(platform2, home, port = 7300, entry = installingEntry()) {
|
|
|
31551
31635
|
const systemApps = "/Applications";
|
|
31552
31636
|
let base = home;
|
|
31553
31637
|
try {
|
|
31554
|
-
|
|
31638
|
+
fs24.accessSync(systemApps, fs24.constants.W_OK);
|
|
31555
31639
|
base = "";
|
|
31556
31640
|
} catch {
|
|
31557
31641
|
}
|
|
@@ -31628,7 +31712,7 @@ function planLauncher(platform2, home, port = 7300, entry = installingEntry()) {
|
|
|
31628
31712
|
function launcherInstalled(port = 7300, platform2 = process.platform, home = os4.homedir()) {
|
|
31629
31713
|
try {
|
|
31630
31714
|
const plan = planLauncher(platform2, home, port, installingEntry());
|
|
31631
|
-
return plan.files.every((f) =>
|
|
31715
|
+
return plan.files.every((f) => fs24.existsSync(f.path));
|
|
31632
31716
|
} catch {
|
|
31633
31717
|
return false;
|
|
31634
31718
|
}
|
|
@@ -31639,8 +31723,8 @@ function installLauncher(argv = []) {
|
|
|
31639
31723
|
const plan = planLauncher(process.platform, os4.homedir(), port, installingEntry());
|
|
31640
31724
|
try {
|
|
31641
31725
|
for (const f of plan.files) {
|
|
31642
|
-
|
|
31643
|
-
|
|
31726
|
+
fs24.mkdirSync(path22.dirname(f.path), { recursive: true });
|
|
31727
|
+
fs24.writeFileSync(f.path, f.contents, { mode: f.executable ? 493 : 420 });
|
|
31644
31728
|
}
|
|
31645
31729
|
} catch (e) {
|
|
31646
31730
|
process.stderr.write(`
|
|
@@ -31660,16 +31744,66 @@ function installLauncher(argv = []) {
|
|
|
31660
31744
|
].join("\n"));
|
|
31661
31745
|
return 0;
|
|
31662
31746
|
}
|
|
31663
|
-
var
|
|
31747
|
+
var fs24, os4, path22;
|
|
31664
31748
|
var init_launcher = __esm({
|
|
31665
31749
|
"src/launcher.ts"() {
|
|
31666
31750
|
"use strict";
|
|
31667
|
-
|
|
31751
|
+
fs24 = __toESM(require("fs"));
|
|
31668
31752
|
os4 = __toESM(require("os"));
|
|
31669
31753
|
path22 = __toESM(require("path"));
|
|
31670
31754
|
}
|
|
31671
31755
|
});
|
|
31672
31756
|
|
|
31757
|
+
// src/npx-cache-check.ts
|
|
31758
|
+
function defaultNpxCacheDir(env = process.env, platform2 = process.platform, home = os5.homedir()) {
|
|
31759
|
+
const configured = env.npm_config_cache?.trim();
|
|
31760
|
+
const cache = configured ? configured : platform2 === "win32" ? path23.join(env.LOCALAPPDATA?.trim() || path23.join(home, "AppData", "Local"), "npm-cache") : path23.join(home, ".npm");
|
|
31761
|
+
return path23.join(cache, "_npx");
|
|
31762
|
+
}
|
|
31763
|
+
function damagedNpxDownloads(npxDir) {
|
|
31764
|
+
let entries;
|
|
31765
|
+
try {
|
|
31766
|
+
entries = fs25.readdirSync(npxDir, { withFileTypes: true });
|
|
31767
|
+
} catch {
|
|
31768
|
+
return [];
|
|
31769
|
+
}
|
|
31770
|
+
const damaged = [];
|
|
31771
|
+
const gone = (p) => {
|
|
31772
|
+
try {
|
|
31773
|
+
fs25.statSync(p);
|
|
31774
|
+
return false;
|
|
31775
|
+
} catch (e) {
|
|
31776
|
+
return e.code === "ENOENT";
|
|
31777
|
+
}
|
|
31778
|
+
};
|
|
31779
|
+
for (const entry of entries) {
|
|
31780
|
+
const dir = path23.join(npxDir, entry.name);
|
|
31781
|
+
const pkg2 = path23.join(dir, PACKAGE_SUBDIR);
|
|
31782
|
+
try {
|
|
31783
|
+
if (!fs25.statSync(dir).isDirectory() || !fs25.statSync(pkg2).isDirectory()) continue;
|
|
31784
|
+
} catch {
|
|
31785
|
+
continue;
|
|
31786
|
+
}
|
|
31787
|
+
if (gone(path23.join(dir, "package.json")) || gone(path23.join(pkg2, "package.json"))) damaged.push(dir);
|
|
31788
|
+
}
|
|
31789
|
+
return damaged.sort();
|
|
31790
|
+
}
|
|
31791
|
+
function damagedDownloadLine(dirs) {
|
|
31792
|
+
if (dirs.length === 0) return null;
|
|
31793
|
+
const one = dirs.length === 1;
|
|
31794
|
+
return `${one ? "One earlier download" : "Earlier downloads"} of GHL Command on this computer ${one ? "is" : "are"} damaged, which makes Claude say "Connection closed" instead of starting it. Delete ${one ? "this folder" : "these folders"}, then start Claude again: ${dirs.join(" and ")}`;
|
|
31795
|
+
}
|
|
31796
|
+
var fs25, os5, path23, PACKAGE_SUBDIR;
|
|
31797
|
+
var init_npx_cache_check = __esm({
|
|
31798
|
+
"src/npx-cache-check.ts"() {
|
|
31799
|
+
"use strict";
|
|
31800
|
+
fs25 = __toESM(require("node:fs"));
|
|
31801
|
+
os5 = __toESM(require("node:os"));
|
|
31802
|
+
path23 = __toESM(require("node:path"));
|
|
31803
|
+
PACKAGE_SUBDIR = path23.join("node_modules", "@elitedcs", "ghl-mcp");
|
|
31804
|
+
}
|
|
31805
|
+
});
|
|
31806
|
+
|
|
31673
31807
|
// src/config-installer.ts
|
|
31674
31808
|
var config_installer_exports = {};
|
|
31675
31809
|
__export(config_installer_exports, {
|
|
@@ -31687,44 +31821,45 @@ __export(config_installer_exports, {
|
|
|
31687
31821
|
});
|
|
31688
31822
|
function defaultIO() {
|
|
31689
31823
|
return {
|
|
31824
|
+
npxCacheDir: defaultNpxCacheDir(),
|
|
31690
31825
|
out: (l) => process.stdout.write(l + "\n"),
|
|
31691
31826
|
err: (l) => process.stderr.write(l + "\n"),
|
|
31692
|
-
rename: (from, to) =>
|
|
31827
|
+
rename: (from, to) => fs26.renameSync(from, to),
|
|
31693
31828
|
sleep: (ms) => new Promise((r) => setTimeout(r, ms))
|
|
31694
31829
|
};
|
|
31695
31830
|
}
|
|
31696
31831
|
function candidateConfigPaths(opts) {
|
|
31697
31832
|
const platform2 = opts?.platform ?? process.platform;
|
|
31698
|
-
const home = opts?.home ??
|
|
31833
|
+
const home = opts?.home ?? os6.homedir();
|
|
31699
31834
|
const file2 = "claude_desktop_config.json";
|
|
31700
31835
|
const candidates = [];
|
|
31701
31836
|
if (platform2 === "darwin") {
|
|
31702
|
-
candidates.push(
|
|
31837
|
+
candidates.push(path24.join(home, "Library", "Application Support", "Claude", file2));
|
|
31703
31838
|
} else if (platform2 === "win32") {
|
|
31704
|
-
const appData = opts?.appData ?? process.env.APPDATA ??
|
|
31705
|
-
candidates.push(
|
|
31706
|
-
const localAppData = opts?.localAppData ?? process.env.LOCALAPPDATA ??
|
|
31707
|
-
const packagesDir =
|
|
31839
|
+
const appData = opts?.appData ?? process.env.APPDATA ?? path24.join(home, "AppData", "Roaming");
|
|
31840
|
+
candidates.push(path24.join(appData, "Claude", file2));
|
|
31841
|
+
const localAppData = opts?.localAppData ?? process.env.LOCALAPPDATA ?? path24.join(home, "AppData", "Local");
|
|
31842
|
+
const packagesDir = path24.join(localAppData, "Packages");
|
|
31708
31843
|
try {
|
|
31709
|
-
for (const entry of
|
|
31844
|
+
for (const entry of fs26.readdirSync(packagesDir)) {
|
|
31710
31845
|
if (entry.startsWith("Claude_")) {
|
|
31711
|
-
candidates.push(
|
|
31846
|
+
candidates.push(path24.join(packagesDir, entry, "LocalCache", "Roaming", "Claude", file2));
|
|
31712
31847
|
}
|
|
31713
31848
|
}
|
|
31714
31849
|
} catch {
|
|
31715
31850
|
}
|
|
31716
31851
|
} else {
|
|
31717
|
-
candidates.push(
|
|
31852
|
+
candidates.push(path24.join(home, ".config", "Claude", file2));
|
|
31718
31853
|
}
|
|
31719
31854
|
return candidates;
|
|
31720
31855
|
}
|
|
31721
31856
|
function resolveConfigPath(explicitPath) {
|
|
31722
31857
|
if (explicitPath) {
|
|
31723
|
-
const p =
|
|
31724
|
-
return { configPath: p, exists:
|
|
31858
|
+
const p = path24.resolve(explicitPath);
|
|
31859
|
+
return { configPath: p, exists: fs26.existsSync(p) };
|
|
31725
31860
|
}
|
|
31726
31861
|
const candidates = candidateConfigPaths();
|
|
31727
|
-
const existing = candidates.filter((c) =>
|
|
31862
|
+
const existing = candidates.filter((c) => fs26.existsSync(c));
|
|
31728
31863
|
if (existing.length > 1) {
|
|
31729
31864
|
throw new InstallStop(
|
|
31730
31865
|
EXIT_REFUSED,
|
|
@@ -31738,25 +31873,25 @@ function resolveConfigPath(explicitPath) {
|
|
|
31738
31873
|
if (existing.length === 1) return { configPath: existing[0], exists: true };
|
|
31739
31874
|
return { configPath: candidates[0], exists: false };
|
|
31740
31875
|
}
|
|
31741
|
-
function resolveSymlinkPolicy(configPath, explicitPath, home =
|
|
31876
|
+
function resolveSymlinkPolicy(configPath, explicitPath, home = os6.homedir()) {
|
|
31742
31877
|
let st;
|
|
31743
31878
|
try {
|
|
31744
|
-
st =
|
|
31879
|
+
st = fs26.lstatSync(configPath);
|
|
31745
31880
|
} catch {
|
|
31746
31881
|
return configPath;
|
|
31747
31882
|
}
|
|
31748
31883
|
if (!st.isSymbolicLink()) return configPath;
|
|
31749
31884
|
let real;
|
|
31750
31885
|
try {
|
|
31751
|
-
real =
|
|
31886
|
+
real = fs26.realpathSync(configPath);
|
|
31752
31887
|
} catch {
|
|
31753
31888
|
throw new InstallStop(
|
|
31754
31889
|
EXIT_REFUSED,
|
|
31755
31890
|
`${configPath} is a link that points to a file that does not exist. Fix or remove the link, or run again with --path to a real file. Nothing was changed.`
|
|
31756
31891
|
);
|
|
31757
31892
|
}
|
|
31758
|
-
const rel =
|
|
31759
|
-
const outsideHome = rel.startsWith("..") ||
|
|
31893
|
+
const rel = path24.relative(path24.resolve(home), real);
|
|
31894
|
+
const outsideHome = rel.startsWith("..") || path24.isAbsolute(rel);
|
|
31760
31895
|
if (outsideHome && !explicitPath) {
|
|
31761
31896
|
throw new InstallStop(
|
|
31762
31897
|
EXIT_REFUSED,
|
|
@@ -31766,8 +31901,8 @@ function resolveSymlinkPolicy(configPath, explicitPath, home = os5.homedir()) {
|
|
|
31766
31901
|
return real;
|
|
31767
31902
|
}
|
|
31768
31903
|
function readConfigBytes(configPath) {
|
|
31769
|
-
const bytes =
|
|
31770
|
-
const stat =
|
|
31904
|
+
const bytes = fs26.readFileSync(configPath);
|
|
31905
|
+
const stat = fs26.statSync(configPath);
|
|
31771
31906
|
if (bytes.length >= 2 && (bytes[0] === 255 && bytes[1] === 254 || bytes[0] === 254 && bytes[1] === 255)) {
|
|
31772
31907
|
throw new InstallStop(
|
|
31773
31908
|
EXIT_REFUSED,
|
|
@@ -31841,23 +31976,23 @@ function backupStamp(now) {
|
|
|
31841
31976
|
}
|
|
31842
31977
|
function chooseBackupPath(configPath, now = /* @__PURE__ */ new Date()) {
|
|
31843
31978
|
const base = `${configPath}.bak-${backupStamp(now)}`;
|
|
31844
|
-
if (!
|
|
31979
|
+
if (!fs26.existsSync(base)) return base;
|
|
31845
31980
|
for (let i = 2; ; i++) {
|
|
31846
31981
|
const candidate = `${base}-${i}`;
|
|
31847
|
-
if (!
|
|
31982
|
+
if (!fs26.existsSync(candidate)) return candidate;
|
|
31848
31983
|
}
|
|
31849
31984
|
}
|
|
31850
31985
|
function writeVerifiedBackup(configPath, originalBytes) {
|
|
31851
31986
|
const backupPath = chooseBackupPath(configPath);
|
|
31852
31987
|
try {
|
|
31853
|
-
|
|
31854
|
-
const readBack =
|
|
31988
|
+
fs26.writeFileSync(backupPath, originalBytes);
|
|
31989
|
+
const readBack = fs26.readFileSync(backupPath);
|
|
31855
31990
|
if (!readBack.equals(originalBytes)) {
|
|
31856
31991
|
throw new Error("backup read-back did not match");
|
|
31857
31992
|
}
|
|
31858
31993
|
} catch (e) {
|
|
31859
31994
|
try {
|
|
31860
|
-
|
|
31995
|
+
fs26.rmSync(backupPath, { force: true });
|
|
31861
31996
|
} catch {
|
|
31862
31997
|
}
|
|
31863
31998
|
throw new InstallStop(
|
|
@@ -31876,9 +32011,9 @@ function baselineOf(bytes, stat) {
|
|
|
31876
32011
|
function assertNotStale(configPath, baseline) {
|
|
31877
32012
|
let ok = false;
|
|
31878
32013
|
try {
|
|
31879
|
-
const st =
|
|
32014
|
+
const st = fs26.statSync(configPath);
|
|
31880
32015
|
if (st.size === baseline.size && st.mtimeMs === baseline.mtimeMs) {
|
|
31881
|
-
ok = sha256(
|
|
32016
|
+
ok = sha256(fs26.readFileSync(configPath)) === baseline.hash;
|
|
31882
32017
|
} else {
|
|
31883
32018
|
ok = false;
|
|
31884
32019
|
}
|
|
@@ -31893,14 +32028,14 @@ function assertNotStale(configPath, baseline) {
|
|
|
31893
32028
|
}
|
|
31894
32029
|
}
|
|
31895
32030
|
async function atomicReplace(opts) {
|
|
31896
|
-
const dir =
|
|
31897
|
-
const tempPath =
|
|
31898
|
-
const fd =
|
|
32031
|
+
const dir = path24.dirname(opts.configPath);
|
|
32032
|
+
const tempPath = path24.join(dir, `.${path24.basename(opts.configPath)}.tmp-${process.pid}-${(0, import_node_crypto7.randomBytes)(4).toString("hex")}`);
|
|
32033
|
+
const fd = fs26.openSync(tempPath, "w");
|
|
31899
32034
|
try {
|
|
31900
|
-
|
|
31901
|
-
|
|
32035
|
+
fs26.writeFileSync(fd, opts.newBytes);
|
|
32036
|
+
fs26.fsyncSync(fd);
|
|
31902
32037
|
} finally {
|
|
31903
|
-
|
|
32038
|
+
fs26.closeSync(fd);
|
|
31904
32039
|
}
|
|
31905
32040
|
try {
|
|
31906
32041
|
for (let attempt = 0; ; attempt++) {
|
|
@@ -31925,21 +32060,27 @@ async function atomicReplace(opts) {
|
|
|
31925
32060
|
}
|
|
31926
32061
|
} catch (e) {
|
|
31927
32062
|
try {
|
|
31928
|
-
|
|
32063
|
+
fs26.rmSync(tempPath, { force: true });
|
|
31929
32064
|
} catch {
|
|
31930
32065
|
}
|
|
31931
32066
|
throw e;
|
|
31932
32067
|
}
|
|
31933
32068
|
try {
|
|
31934
|
-
const dirFd =
|
|
32069
|
+
const dirFd = fs26.openSync(dir, "r");
|
|
31935
32070
|
try {
|
|
31936
|
-
|
|
32071
|
+
fs26.fsyncSync(dirFd);
|
|
31937
32072
|
} finally {
|
|
31938
|
-
|
|
32073
|
+
fs26.closeSync(dirFd);
|
|
31939
32074
|
}
|
|
31940
32075
|
} catch {
|
|
31941
32076
|
}
|
|
31942
32077
|
}
|
|
32078
|
+
function reportDamagedDownloads(io) {
|
|
32079
|
+
const line3 = damagedDownloadLine(damagedNpxDownloads(io.npxCacheDir));
|
|
32080
|
+
if (!line3) return;
|
|
32081
|
+
io.out("");
|
|
32082
|
+
io.out(line3);
|
|
32083
|
+
}
|
|
31943
32084
|
function successReport(io, args) {
|
|
31944
32085
|
io.out("Added GHL Command to Claude Desktop.");
|
|
31945
32086
|
io.out("");
|
|
@@ -31987,7 +32128,7 @@ async function runInstall(argv, ioOverride) {
|
|
|
31987
32128
|
try {
|
|
31988
32129
|
const resolved2 = resolveConfigPath(flags.path);
|
|
31989
32130
|
const configPath = resolveSymlinkPolicy(resolved2.configPath, flags.path !== void 0);
|
|
31990
|
-
const exists =
|
|
32131
|
+
const exists = fs26.existsSync(configPath);
|
|
31991
32132
|
let read2 = null;
|
|
31992
32133
|
let outcome;
|
|
31993
32134
|
if (exists) {
|
|
@@ -31999,6 +32140,7 @@ async function runInstall(argv, ioOverride) {
|
|
|
31999
32140
|
const { merged, keptServers, action } = outcome.tier === 3 ? { merged: { mcpServers: { [SERVER_KEY]: { command: DESIRED_ENTRY.command, args: [...DESIRED_ENTRY.args] } } }, keptServers: [], action: "added" } : mergeGhlEntry(outcome.root, flags.force === true);
|
|
32000
32141
|
if (action === "identical") {
|
|
32001
32142
|
io.out("GHL Command is already set up in Claude Desktop \u2014 nothing to do.");
|
|
32143
|
+
reportDamagedDownloads(io);
|
|
32002
32144
|
return EXIT_OK;
|
|
32003
32145
|
}
|
|
32004
32146
|
const newText = JSON.stringify(merged, null, 2) + "\n";
|
|
@@ -32014,10 +32156,11 @@ async function runInstall(argv, ioOverride) {
|
|
|
32014
32156
|
if (exists) io.out(`Would back up: ${chooseBackupPath(configPath)}`);
|
|
32015
32157
|
if (outcome.tier === 2) io.out("Would rewrite the file in a cleaner format (original kept as the backup).");
|
|
32016
32158
|
if (outcome.tier === 3) io.out("Would write a fresh settings file (original kept as the backup).");
|
|
32159
|
+
reportDamagedDownloads(io);
|
|
32017
32160
|
return EXIT_OK;
|
|
32018
32161
|
}
|
|
32019
32162
|
const backupPath = exists && read2 ? writeVerifiedBackup(configPath, read2.bytes) : null;
|
|
32020
|
-
if (!exists)
|
|
32163
|
+
if (!exists) fs26.mkdirSync(path24.dirname(configPath), { recursive: true });
|
|
32021
32164
|
await atomicReplace({
|
|
32022
32165
|
configPath,
|
|
32023
32166
|
newBytes,
|
|
@@ -32025,6 +32168,7 @@ async function runInstall(argv, ioOverride) {
|
|
|
32025
32168
|
io
|
|
32026
32169
|
});
|
|
32027
32170
|
successReport(io, { configPath, backupPath, keptServers, tier: outcome.tier, hadOriginal: exists });
|
|
32171
|
+
reportDamagedDownloads(io);
|
|
32028
32172
|
return EXIT_OK;
|
|
32029
32173
|
} catch (e) {
|
|
32030
32174
|
if (e instanceof InstallStop) {
|
|
@@ -32035,16 +32179,17 @@ async function runInstall(argv, ioOverride) {
|
|
|
32035
32179
|
return EXIT_ABORTED;
|
|
32036
32180
|
}
|
|
32037
32181
|
}
|
|
32038
|
-
var
|
|
32182
|
+
var fs26, os6, path24, import_node_crypto7, import_node_util, import_json5, SERVER_KEY, DESIRED_ENTRY, EXIT_OK, EXIT_USAGE, EXIT_REFUSED, EXIT_ABORTED, RENAME_RETRY_DELAYS_MS, LOCK_ERROR_CODES, BOM_UTF8, InstallStop;
|
|
32039
32183
|
var init_config_installer = __esm({
|
|
32040
32184
|
"src/config-installer.ts"() {
|
|
32041
32185
|
"use strict";
|
|
32042
|
-
|
|
32043
|
-
|
|
32044
|
-
|
|
32186
|
+
fs26 = __toESM(require("node:fs"));
|
|
32187
|
+
os6 = __toESM(require("node:os"));
|
|
32188
|
+
path24 = __toESM(require("node:path"));
|
|
32045
32189
|
import_node_crypto7 = require("node:crypto");
|
|
32046
32190
|
import_node_util = require("node:util");
|
|
32047
32191
|
import_json5 = __toESM(require("json5"));
|
|
32192
|
+
init_npx_cache_check();
|
|
32048
32193
|
SERVER_KEY = "ghl";
|
|
32049
32194
|
DESIRED_ENTRY = Object.freeze({
|
|
32050
32195
|
command: "npx",
|
|
@@ -32074,9 +32219,9 @@ function errLine(msg3) {
|
|
|
32074
32219
|
function preflightWritable() {
|
|
32075
32220
|
try {
|
|
32076
32221
|
const dir = ensureAppDataDir();
|
|
32077
|
-
const probe =
|
|
32078
|
-
|
|
32079
|
-
|
|
32222
|
+
const probe = path25.join(dir, `.write-probe.${process.pid}.${(0, import_crypto2.randomBytes)(4).toString("hex")}`);
|
|
32223
|
+
fs27.writeFileSync(probe, "ok");
|
|
32224
|
+
fs27.unlinkSync(probe);
|
|
32080
32225
|
return true;
|
|
32081
32226
|
} catch (error) {
|
|
32082
32227
|
errLine(`Config dir is not writable: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -32264,7 +32409,7 @@ async function cmdRegisterAgencyKey(argv, registry2) {
|
|
|
32264
32409
|
return EXIT_FS;
|
|
32265
32410
|
}
|
|
32266
32411
|
if (!confirmSaved(registry2)) return EXIT_FS;
|
|
32267
|
-
process.stdout.write(`Registered agency key
|
|
32412
|
+
process.stdout.write(`Registered agency key (${keyHint(apiKey2)}) \u2192 ${tokenRegistryPath()}
|
|
32268
32413
|
`);
|
|
32269
32414
|
restartReminder();
|
|
32270
32415
|
return EXIT_OK2;
|
|
@@ -32345,13 +32490,13 @@ async function runCli(subcommand, argv) {
|
|
|
32345
32490
|
return EXIT_USAGE2;
|
|
32346
32491
|
}
|
|
32347
32492
|
}
|
|
32348
|
-
var import_node_util2,
|
|
32493
|
+
var import_node_util2, fs27, path25, import_crypto2, EXIT_OK2, EXIT_USAGE2, EXIT_VALIDATION, EXIT_FS, USAGE;
|
|
32349
32494
|
var init_cli = __esm({
|
|
32350
32495
|
"src/cli.ts"() {
|
|
32351
32496
|
"use strict";
|
|
32352
32497
|
import_node_util2 = require("node:util");
|
|
32353
|
-
|
|
32354
|
-
|
|
32498
|
+
fs27 = __toESM(require("fs"));
|
|
32499
|
+
path25 = __toESM(require("path"));
|
|
32355
32500
|
import_crypto2 = require("crypto");
|
|
32356
32501
|
init_ghl_client();
|
|
32357
32502
|
init_token_registry();
|
|
@@ -32877,11 +33022,11 @@ __export(intake_overlay_exports, {
|
|
|
32877
33022
|
writeOverlay: () => writeOverlay
|
|
32878
33023
|
});
|
|
32879
33024
|
function overlayPath() {
|
|
32880
|
-
return
|
|
33025
|
+
return path26.join(appDataDir(), "intake-overlay.json");
|
|
32881
33026
|
}
|
|
32882
33027
|
function readOverlay() {
|
|
32883
33028
|
try {
|
|
32884
|
-
const raw = JSON.parse(
|
|
33029
|
+
const raw = JSON.parse(fs28.readFileSync(overlayPath(), "utf8"));
|
|
32885
33030
|
if (raw && typeof raw === "object") return raw;
|
|
32886
33031
|
} catch {
|
|
32887
33032
|
}
|
|
@@ -32889,14 +33034,14 @@ function readOverlay() {
|
|
|
32889
33034
|
}
|
|
32890
33035
|
function writeOverlay(layer) {
|
|
32891
33036
|
ensureAppDataDir();
|
|
32892
|
-
|
|
33037
|
+
fs28.writeFileSync(overlayPath(), JSON.stringify(layer, null, 2), { mode: 384 });
|
|
32893
33038
|
}
|
|
32894
|
-
var
|
|
33039
|
+
var fs28, path26;
|
|
32895
33040
|
var init_intake_overlay = __esm({
|
|
32896
33041
|
"src/intake-overlay.ts"() {
|
|
32897
33042
|
"use strict";
|
|
32898
|
-
|
|
32899
|
-
|
|
33043
|
+
fs28 = __toESM(require("fs"));
|
|
33044
|
+
path26 = __toESM(require("path"));
|
|
32900
33045
|
init_credentials_store();
|
|
32901
33046
|
}
|
|
32902
33047
|
});
|
|
@@ -34135,11 +34280,11 @@ function recoverStuckStages(state) {
|
|
|
34135
34280
|
return { state: { ...state, clients }, recovered };
|
|
34136
34281
|
}
|
|
34137
34282
|
function cockpitStatePath() {
|
|
34138
|
-
return
|
|
34283
|
+
return path27.join(appDataDir(), "cockpit-state.json");
|
|
34139
34284
|
}
|
|
34140
34285
|
function readCockpitState() {
|
|
34141
34286
|
try {
|
|
34142
|
-
const raw = JSON.parse(
|
|
34287
|
+
const raw = JSON.parse(fs29.readFileSync(cockpitStatePath(), "utf8"));
|
|
34143
34288
|
if (raw && raw.v === 1 && raw.clients && typeof raw.clients === "object") return raw;
|
|
34144
34289
|
} catch {
|
|
34145
34290
|
}
|
|
@@ -34147,7 +34292,7 @@ function readCockpitState() {
|
|
|
34147
34292
|
}
|
|
34148
34293
|
function writeCockpitState(state) {
|
|
34149
34294
|
ensureAppDataDir();
|
|
34150
|
-
|
|
34295
|
+
fs29.writeFileSync(cockpitStatePath(), JSON.stringify(state, null, 2), { mode: 384 });
|
|
34151
34296
|
}
|
|
34152
34297
|
function setStage(state, locationId2, stageIndex, status) {
|
|
34153
34298
|
if (!Number.isInteger(stageIndex) || stageIndex < 0 || stageIndex >= STAGES2.length) throw new Error("bad stage index");
|
|
@@ -34424,7 +34569,7 @@ function notify(message) {
|
|
|
34424
34569
|
`);
|
|
34425
34570
|
try {
|
|
34426
34571
|
ensureAppDataDir();
|
|
34427
|
-
|
|
34572
|
+
fs29.appendFileSync(path27.join(appDataDir(), "cockpit.log"), `[${(/* @__PURE__ */ new Date()).toISOString()}] ${message}
|
|
34428
34573
|
`);
|
|
34429
34574
|
} catch {
|
|
34430
34575
|
}
|
|
@@ -34494,20 +34639,20 @@ function withCockpitShell(page, screen, extraHead = "") {
|
|
|
34494
34639
|
function clientDocsDir(locationId2, base = appDataDir()) {
|
|
34495
34640
|
const safeLoc = String(locationId2).replace(/[^A-Za-z0-9_-]/g, "");
|
|
34496
34641
|
if (!safeLoc) throw new Error("no client");
|
|
34497
|
-
return
|
|
34642
|
+
return path27.join(base, "clients", safeLoc, "documents");
|
|
34498
34643
|
}
|
|
34499
34644
|
function safeDocName(name) {
|
|
34500
|
-
const cleaned =
|
|
34501
|
-
const ext =
|
|
34645
|
+
const cleaned = path27.basename(String(name ?? "")).replace(/[^A-Za-z0-9._ -]/g, "_").replace(/\s+/g, " ").trim();
|
|
34646
|
+
const ext = path27.extname(cleaned).toLowerCase();
|
|
34502
34647
|
if (!cleaned || cleaned.startsWith(".") || !DOC_EXTS.has(ext)) throw new Error("Only plain text (.txt, .md, .csv), .vtt and .srt files can be saved here.");
|
|
34503
34648
|
const stem = cleaned.slice(0, cleaned.length - ext.length).slice(0, 120 - ext.length).trim();
|
|
34504
34649
|
return `${stem || "document"}${ext}`;
|
|
34505
34650
|
}
|
|
34506
34651
|
function listClientDocs(locationId2, base = appDataDir()) {
|
|
34507
34652
|
const dir = clientDocsDir(locationId2, base);
|
|
34508
|
-
if (!
|
|
34509
|
-
return
|
|
34510
|
-
const st =
|
|
34653
|
+
if (!fs29.existsSync(dir)) return [];
|
|
34654
|
+
return fs29.readdirSync(dir).filter((f) => DOC_EXTS.has(path27.extname(f).toLowerCase())).map((f) => {
|
|
34655
|
+
const st = fs29.statSync(path27.join(dir, f));
|
|
34511
34656
|
return { name: f, bytes: st.size, savedAt: st.mtime.toISOString() };
|
|
34512
34657
|
}).sort((a, b) => b.savedAt.localeCompare(a.savedAt));
|
|
34513
34658
|
}
|
|
@@ -34525,7 +34670,7 @@ function readFailureText(e) {
|
|
|
34525
34670
|
function logQuietly(message) {
|
|
34526
34671
|
try {
|
|
34527
34672
|
ensureAppDataDir();
|
|
34528
|
-
|
|
34673
|
+
fs29.appendFileSync(path27.join(appDataDir(), "cockpit.log"), `[${(/* @__PURE__ */ new Date()).toISOString()}] ${message}
|
|
34529
34674
|
`);
|
|
34530
34675
|
} catch {
|
|
34531
34676
|
}
|
|
@@ -34598,40 +34743,40 @@ function newClientFailure(error, surface = "command-os", account2) {
|
|
|
34598
34743
|
}
|
|
34599
34744
|
function insideDir(dir, p) {
|
|
34600
34745
|
try {
|
|
34601
|
-
const real =
|
|
34602
|
-
const base =
|
|
34603
|
-
return real === base || real.startsWith(base +
|
|
34746
|
+
const real = fs29.realpathSync(p);
|
|
34747
|
+
const base = fs29.realpathSync(dir);
|
|
34748
|
+
return real === base || real.startsWith(base + path27.sep);
|
|
34604
34749
|
} catch {
|
|
34605
34750
|
return false;
|
|
34606
34751
|
}
|
|
34607
34752
|
}
|
|
34608
34753
|
function uniqueDocName(dir, name) {
|
|
34609
|
-
const ext =
|
|
34754
|
+
const ext = path27.extname(name), stem = name.slice(0, name.length - ext.length);
|
|
34610
34755
|
let candidate = name;
|
|
34611
|
-
for (let n = 2;
|
|
34756
|
+
for (let n = 2; fs29.existsSync(path27.join(dir, candidate)); n++) candidate = `${stem} (${n})${ext}`;
|
|
34612
34757
|
return candidate;
|
|
34613
34758
|
}
|
|
34614
34759
|
function writeDocAtomically(dir, name, bytes) {
|
|
34615
|
-
|
|
34760
|
+
fs29.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
34616
34761
|
const finalName = uniqueDocName(dir, name);
|
|
34617
|
-
const tmp =
|
|
34618
|
-
const fd =
|
|
34762
|
+
const tmp = path27.join(dir, `.${finalName}.${process.pid}.tmp`);
|
|
34763
|
+
const fd = fs29.openSync(tmp, fs29.constants.O_WRONLY | fs29.constants.O_CREAT | fs29.constants.O_EXCL | (fs29.constants.O_NOFOLLOW ?? 0), 384);
|
|
34619
34764
|
try {
|
|
34620
|
-
|
|
34621
|
-
|
|
34765
|
+
fs29.writeSync(fd, bytes);
|
|
34766
|
+
fs29.fsyncSync(fd);
|
|
34622
34767
|
} finally {
|
|
34623
|
-
|
|
34768
|
+
fs29.closeSync(fd);
|
|
34624
34769
|
}
|
|
34625
|
-
const dest =
|
|
34770
|
+
const dest = path27.join(dir, finalName);
|
|
34626
34771
|
try {
|
|
34627
|
-
if (
|
|
34628
|
-
|
|
34772
|
+
if (fs29.lstatSync(dest).isSymbolicLink()) {
|
|
34773
|
+
fs29.unlinkSync(tmp);
|
|
34629
34774
|
throw new Error("that name is taken by a link");
|
|
34630
34775
|
}
|
|
34631
34776
|
} catch (e) {
|
|
34632
34777
|
if (e.code !== "ENOENT") throw e;
|
|
34633
34778
|
}
|
|
34634
|
-
|
|
34779
|
+
fs29.renameSync(tmp, dest);
|
|
34635
34780
|
return finalName;
|
|
34636
34781
|
}
|
|
34637
34782
|
function readableAccount(locationId2, roster = currentRosterRef(), state = readCockpitState()) {
|
|
@@ -35880,8 +36025,23 @@ function cockpitHeader(req, name) {
|
|
|
35880
36025
|
const value = req.headers[name];
|
|
35881
36026
|
return Array.isArray(value) ? value.join(",") : value ?? "";
|
|
35882
36027
|
}
|
|
36028
|
+
function headerFields(req, name) {
|
|
36029
|
+
if (req instanceof http.IncomingMessage || "rawHeaders" in req) {
|
|
36030
|
+
const raw = req.rawHeaders;
|
|
36031
|
+
if (!Array.isArray(raw)) return [];
|
|
36032
|
+
const values = [];
|
|
36033
|
+
for (let i = 0; i + 1 < raw.length; i += 2) {
|
|
36034
|
+
if (typeof raw[i] === "string" && raw[i].toLowerCase() === name) values.push(String(raw[i + 1]));
|
|
36035
|
+
}
|
|
36036
|
+
return values;
|
|
36037
|
+
}
|
|
36038
|
+
const value = req.headers[name];
|
|
36039
|
+
return value === void 0 ? [] : Array.isArray(value) ? value : [value];
|
|
36040
|
+
}
|
|
35883
36041
|
function isAllowedCockpitHost(req, port) {
|
|
35884
|
-
const
|
|
36042
|
+
const hosts = headerFields(req, "host");
|
|
36043
|
+
if (hosts.length !== 1) return false;
|
|
36044
|
+
const host = hosts[0].toLowerCase();
|
|
35885
36045
|
return host === `127.0.0.1:${port}` || host === `localhost:${port}`;
|
|
35886
36046
|
}
|
|
35887
36047
|
function mayServeSeat(req, port) {
|
|
@@ -35898,8 +36058,10 @@ function checkCockpitPath(req) {
|
|
|
35898
36058
|
return null;
|
|
35899
36059
|
}
|
|
35900
36060
|
function checkCockpitOrigin(req, port) {
|
|
35901
|
-
const
|
|
35902
|
-
if (
|
|
36061
|
+
const origins = headerFields(req, "origin");
|
|
36062
|
+
if (origins.length === 0) return null;
|
|
36063
|
+
const origin = origins.length === 1 ? origins[0].toLowerCase() : "";
|
|
36064
|
+
if (origin !== `http://127.0.0.1:${port}` && origin !== `http://localhost:${port}`) {
|
|
35903
36065
|
return { status: 403, error: "cross-origin request rejected" };
|
|
35904
36066
|
}
|
|
35905
36067
|
return null;
|
|
@@ -35924,7 +36086,8 @@ function cockpitGate(req, seatToken, port) {
|
|
|
35924
36086
|
if (denied) return denied;
|
|
35925
36087
|
const pathname = cockpitPath(req);
|
|
35926
36088
|
if (SEAT_GUARDED_READS.includes(pathname) || pathname === "/api/design" || pathname.startsWith("/api/design/")) {
|
|
35927
|
-
|
|
36089
|
+
const sites = headerFields(req, "sec-fetch-site").map((v) => v.trim().toLowerCase());
|
|
36090
|
+
if (sites.length > 1 || sites.length === 1 && sites[0] !== "same-origin" && sites[0] !== "none") {
|
|
35928
36091
|
return { status: 403, error: "cross-site request rejected" };
|
|
35929
36092
|
}
|
|
35930
36093
|
return checkCockpitOrigin(req, port) ?? checkSeatToken({ method: "POST", headers: req.headers }, seatToken);
|
|
@@ -35961,7 +36124,7 @@ async function runDashboard(argv) {
|
|
|
35961
36124
|
if (await cockpitAlreadyRunning(port)) {
|
|
35962
36125
|
openBrowser(port);
|
|
35963
36126
|
process.stderr.write(`
|
|
35964
|
-
Command OS is already running. Opening
|
|
36127
|
+
Command OS is already running. Opening localhost:${port}
|
|
35965
36128
|
|
|
35966
36129
|
`);
|
|
35967
36130
|
return 0;
|
|
@@ -36266,8 +36429,8 @@ ${opportunityNoteFailure?.detail ?? ""}`);
|
|
|
36266
36429
|
if (!bytes.length) throw new Error("that file is empty");
|
|
36267
36430
|
if (bytes.length > DOC_MAX_BYTES) throw new Error("that file is over 20 MB");
|
|
36268
36431
|
const dir = clientDocsDir(args.locationId);
|
|
36269
|
-
|
|
36270
|
-
if (!insideDir(
|
|
36432
|
+
fs29.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
36433
|
+
if (!insideDir(path27.join(appDataDir(), "clients"), dir)) throw new Error("This client's documents folder is not where it should be, so nothing was saved.");
|
|
36271
36434
|
const saved = writeDocAtomically(dir, name, bytes);
|
|
36272
36435
|
res.end(JSON.stringify({ ok: true, name: saved, files: listClientDocs(args.locationId) }));
|
|
36273
36436
|
} catch (e) {
|
|
@@ -36614,10 +36777,10 @@ ${opportunityNoteFailure?.detail ?? ""}`);
|
|
|
36614
36777
|
if (args.file) {
|
|
36615
36778
|
const name = safeDocName(args.file);
|
|
36616
36779
|
const dir = clientDocsDir(args.locationId);
|
|
36617
|
-
const p =
|
|
36618
|
-
if (!
|
|
36619
|
-
if (
|
|
36620
|
-
const text =
|
|
36780
|
+
const p = path27.join(dir, name);
|
|
36781
|
+
if (!fs29.existsSync(p)) throw new Error("that document is not saved for this client");
|
|
36782
|
+
if (fs29.lstatSync(p).isSymbolicLink() || !insideDir(dir, p)) throw new Error("that document is not saved for this client");
|
|
36783
|
+
const text = fs29.readFileSync(p, "utf8");
|
|
36621
36784
|
if (text.length > 6e4) throw new Error("The limit is 60,000 characters. Paste an excerpt or shorten this text file.");
|
|
36622
36785
|
fileSource = { kind: "text", text };
|
|
36623
36786
|
}
|
|
@@ -36625,7 +36788,8 @@ ${opportunityNoteFailure?.detail ?? ""}`);
|
|
|
36625
36788
|
let recorderPatterns = [];
|
|
36626
36789
|
if (source.kind === "recorder") {
|
|
36627
36790
|
recorderPatterns = await new Promise((resolve12) => {
|
|
36628
|
-
const
|
|
36791
|
+
const mcpList = claudeCommand(claudeBin(), ["mcp", "list"]);
|
|
36792
|
+
const ls = (0, import_child_process5.spawn)(mcpList.command, mcpList.args, { stdio: ["ignore", "pipe", "ignore"] });
|
|
36629
36793
|
let buf = "";
|
|
36630
36794
|
ls.stdout.on("data", (d) => {
|
|
36631
36795
|
buf += d.toString();
|
|
@@ -36690,14 +36854,14 @@ ${opportunityNoteFailure?.detail ?? ""}`);
|
|
|
36690
36854
|
}
|
|
36691
36855
|
if (req.method === "GET" && /^\/brand\/[a-z0-9-]+\/screenshot\.png$/.test(url)) {
|
|
36692
36856
|
const slug3 = url.split("/")[2];
|
|
36693
|
-
const file2 =
|
|
36694
|
-
if (!
|
|
36857
|
+
const file2 = path27.join(brandDir(slug3), "screenshot.png");
|
|
36858
|
+
if (!fs29.existsSync(file2)) {
|
|
36695
36859
|
res.writeHead(404);
|
|
36696
36860
|
res.end();
|
|
36697
36861
|
return;
|
|
36698
36862
|
}
|
|
36699
36863
|
res.writeHead(200, { "Content-Type": "image/png", "Cache-Control": "no-store" });
|
|
36700
|
-
res.end(
|
|
36864
|
+
res.end(fs29.readFileSync(file2));
|
|
36701
36865
|
return;
|
|
36702
36866
|
}
|
|
36703
36867
|
if (req.method === "GET" && /^\/brand\/[a-z0-9-]+$/.test(url)) {
|
|
@@ -36706,7 +36870,7 @@ ${opportunityNoteFailure?.detail ?? ""}`);
|
|
|
36706
36870
|
const dna = houseLooks().find((d) => d.slug === slug3) ?? readBrand(slug3);
|
|
36707
36871
|
const dir = brandExists(slug3) ? brandDir(slug3) : void 0;
|
|
36708
36872
|
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8", "Cache-Control": "no-store" });
|
|
36709
|
-
res.end(brandCardHtml(dna, { screenshot: dir ?
|
|
36873
|
+
res.end(brandCardHtml(dna, { screenshot: dir ? fs29.existsSync(path27.join(dir, "screenshot.png")) : false, dir, seatToken: mayServeSeat(req, port) ? seatToken : "" }));
|
|
36710
36874
|
} catch (e) {
|
|
36711
36875
|
res.writeHead(404, { "Content-Type": "text/plain; charset=utf-8" });
|
|
36712
36876
|
res.end(e.message);
|
|
@@ -36868,7 +37032,7 @@ ${opportunityNoteFailure?.detail ?? ""}`);
|
|
|
36868
37032
|
}
|
|
36869
37033
|
try {
|
|
36870
37034
|
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
36871
|
-
res.end(
|
|
37035
|
+
res.end(fs29.readFileSync(localCopyPath(locationId2), "utf8"));
|
|
36872
37036
|
} catch {
|
|
36873
37037
|
res.end("The handoff is not prepared yet. Run Verify first.");
|
|
36874
37038
|
}
|
|
@@ -37434,7 +37598,7 @@ ${opportunityNoteFailure?.detail ?? ""}`);
|
|
|
37434
37598
|
}
|
|
37435
37599
|
const roster = currentRoster();
|
|
37436
37600
|
process.stderr.write(`
|
|
37437
|
-
Command OS cockpit running:
|
|
37601
|
+
Command OS cockpit running: localhost:${port}
|
|
37438
37602
|
${roster.length} client${roster.length === 1 ? "" : "s"} on the board. Ctrl-C to stop.
|
|
37439
37603
|
|
|
37440
37604
|
`);
|
|
@@ -37457,7 +37621,7 @@ ${opportunityNoteFailure?.detail ?? ""}`);
|
|
|
37457
37621
|
process.on("SIGTERM", stop);
|
|
37458
37622
|
});
|
|
37459
37623
|
}
|
|
37460
|
-
var import_node_util4,
|
|
37624
|
+
var import_node_util4, fs29, path27, http, import_child_process4, import_child_process5, osmod, STAGES2, ACCOUNT_TYPES, activeRun2, handoffEmailPreviews, UPGRADE_MSG, DOC_EXTS, DOC_MAX_BYTES, NEEDS_AGENCY_KEY, INTAKE_UNREADABLE, INTAKE_FORM_REQUIRED, INTAKE_CONNECT_REQUIRED, INTAKE_OWN_MESSAGES, currentRosterRef, BOARD_ROUTES, BOARD_TITLES, anthropicKeyWrites, SEAT_TOKEN_HEADER, NOT_SIGNED_IN_MSG, SEAT_GUARDED_READS;
|
|
37461
37625
|
var init_dashboard = __esm({
|
|
37462
37626
|
"src/dashboard.ts"() {
|
|
37463
37627
|
"use strict";
|
|
@@ -37476,8 +37640,8 @@ var init_dashboard = __esm({
|
|
|
37476
37640
|
init_handoff_snapshot();
|
|
37477
37641
|
init_member_progress();
|
|
37478
37642
|
init_build_obligations();
|
|
37479
|
-
|
|
37480
|
-
|
|
37643
|
+
fs29 = __toESM(require("fs"));
|
|
37644
|
+
path27 = __toESM(require("path"));
|
|
37481
37645
|
http = __toESM(require("http"));
|
|
37482
37646
|
import_child_process4 = require("child_process");
|
|
37483
37647
|
init_credentials_store();
|
|
@@ -37525,6 +37689,7 @@ var init_dashboard = __esm({
|
|
|
37525
37689
|
init_blueprint_review();
|
|
37526
37690
|
init_intake_overlay();
|
|
37527
37691
|
init_stage_runner();
|
|
37692
|
+
init_claude_locate();
|
|
37528
37693
|
import_child_process5 = require("child_process");
|
|
37529
37694
|
init_ghl_client();
|
|
37530
37695
|
init_client_capabilities();
|
|
@@ -37677,7 +37842,7 @@ GHL Command version: v${mcpVersion}` : "";
|
|
|
37677
37842
|
type: "text",
|
|
37678
37843
|
text: `Current location: ${loc?.name || "Unknown"}
|
|
37679
37844
|
ID: ${client.defaultLocationId}
|
|
37680
|
-
API
|
|
37845
|
+
API key: ${client.getApiKeyHint()}
|
|
37681
37846
|
Address: ${loc?.address || "N/A"}
|
|
37682
37847
|
Email: ${loc?.email || "N/A"}
|
|
37683
37848
|
Token registry: ${registeredCount} location(s) registered${versionLine}`
|
|
@@ -37703,7 +37868,7 @@ Token registry: ${registeredCount} location(s) registered${versionLine}`
|
|
|
37703
37868
|
},
|
|
37704
37869
|
async ({ locationId: locationId2 }) => withSwitchLock(async () => {
|
|
37705
37870
|
const previousId = client.defaultLocationId;
|
|
37706
|
-
const
|
|
37871
|
+
const previousKeyHint = client.getApiKeyHint();
|
|
37707
37872
|
const previousApiKey = client.getApiKey();
|
|
37708
37873
|
const previousBuilderApiKey = builderClient?.getApiKey();
|
|
37709
37874
|
try {
|
|
@@ -37736,7 +37901,7 @@ Token registry: ${registeredCount} location(s) registered${versionLine}`
|
|
|
37736
37901
|
const protectedLine = protectedName ? `
|
|
37737
37902
|
|
|
37738
37903
|
\u{1F512} ${protectedName} is on this machine's protected list. You can read and report on it; nothing here can be created, changed or deleted, and no approval overrides that.` : "";
|
|
37739
|
-
const keyStatus = keySwapped ? `API key swapped: ${
|
|
37904
|
+
const keyStatus = keySwapped ? `API key swapped: ${previousKeyHint} \u2192 ${client.getApiKeyHint()}` : `API key unchanged (${client.getApiKeyHint()})${!registry2?.getToken(locationId2) ? " \u2014 consider using register_location to add this location's key" : ""}`;
|
|
37740
37905
|
return {
|
|
37741
37906
|
content: [
|
|
37742
37907
|
{
|
|
@@ -37840,7 +38005,7 @@ Make sure:
|
|
|
37840
38005
|
content: [
|
|
37841
38006
|
{
|
|
37842
38007
|
type: "text",
|
|
37843
|
-
text: `Agency key ${hadKey ? "replaced" : "registered"}
|
|
38008
|
+
text: `Agency key ${hadKey ? "replaced" : "registered"} (${keyHint(apiKey2)}).
|
|
37844
38009
|
Saved to the token registry.
|
|
37845
38010
|
|
|
37846
38011
|
Agency-wide tools now available: list_snapshots, create_snapshot_share_link, and list_available_locations (across all sub-accounts).`
|
|
@@ -38130,7 +38295,7 @@ ${lines.join("\n")}
|
|
|
38130
38295
|
}
|
|
38131
38296
|
return { content: [{
|
|
38132
38297
|
type: "text",
|
|
38133
|
-
text: `Created ${args.name} (${outcome.locationId}). ` + (outcome.snapshotId ? `Snapshot ${outcome.snapshotId} is loading. GoHighLevel applies its assets over the next minute or two. ` : "No snapshot loaded. ") + (outcome.registered ? "Continue with its intake in GHL Command. " : "In this sub-account, create a Private Integration key in Settings. Register it in GHL Command with its key. ") + `Open
|
|
38298
|
+
text: `Created ${args.name} (${outcome.locationId}). ` + (outcome.snapshotId ? `Snapshot ${outcome.snapshotId} is loading. GoHighLevel applies its assets over the next minute or two. ` : "No snapshot loaded. ") + (outcome.registered ? "Continue with its intake in GHL Command. " : "In this sub-account, create a Private Integration key in Settings. Register it in GHL Command with its key. ") + `Open localhost:7300/new#client=${encodeURIComponent(outcome.locationId)}.`
|
|
38134
38299
|
}] };
|
|
38135
38300
|
}
|
|
38136
38301
|
);
|
|
@@ -39430,13 +39595,13 @@ function buildContactSmartListBody(args) {
|
|
|
39430
39595
|
function registerSmartListTools(server2, builderClient) {
|
|
39431
39596
|
if (!builderClient) return;
|
|
39432
39597
|
const client = builderClient;
|
|
39433
|
-
async function smartListRequest(method,
|
|
39434
|
-
const url = `${SMARTLIST_BASE}${
|
|
39598
|
+
async function smartListRequest(method, path40, body) {
|
|
39599
|
+
const url = `${SMARTLIST_BASE}${path40}`;
|
|
39435
39600
|
const sendsBody = body !== void 0 && (method === "POST" || method === "PUT");
|
|
39436
39601
|
const response = await builderRequest(client, method, url, sendsBody ? { body } : {});
|
|
39437
39602
|
if (!response.ok) {
|
|
39438
39603
|
const text2 = await response.text();
|
|
39439
|
-
throw new Error(`Smart Lists API Error ${response.status}: ${method} ${
|
|
39604
|
+
throw new Error(`Smart Lists API Error ${response.status}: ${method} ${path40}
|
|
39440
39605
|
${text2}`);
|
|
39441
39606
|
}
|
|
39442
39607
|
const text = await response.text();
|
|
@@ -39665,11 +39830,11 @@ var init_smart_lists = __esm({
|
|
|
39665
39830
|
function registerReputationTools(server2, builderClient) {
|
|
39666
39831
|
if (!builderClient) return;
|
|
39667
39832
|
const client = builderClient;
|
|
39668
|
-
async function reputationRequest(method,
|
|
39669
|
-
const response = await builderRequest(client, method, `${REPUTATION_BASE}${
|
|
39833
|
+
async function reputationRequest(method, path40) {
|
|
39834
|
+
const response = await builderRequest(client, method, `${REPUTATION_BASE}${path40}`);
|
|
39670
39835
|
if (!response.ok) {
|
|
39671
39836
|
const text2 = await response.text();
|
|
39672
|
-
throw new Error(`Reputation API Error ${response.status}: ${method} ${
|
|
39837
|
+
throw new Error(`Reputation API Error ${response.status}: ${method} ${path40}
|
|
39673
39838
|
${text2}`);
|
|
39674
39839
|
}
|
|
39675
39840
|
const text = await response.text();
|
|
@@ -39807,11 +39972,11 @@ var init_email_campaigns = __esm({
|
|
|
39807
39972
|
function registerMembershipTools(server2, builderClient) {
|
|
39808
39973
|
if (!builderClient) return;
|
|
39809
39974
|
const client = builderClient;
|
|
39810
|
-
async function membershipRequest(
|
|
39811
|
-
const response = await builderRequest(client, method, `${MEMBERSHIP_BASE2}${
|
|
39975
|
+
async function membershipRequest(path40, method = "GET", body) {
|
|
39976
|
+
const response = await builderRequest(client, method, `${MEMBERSHIP_BASE2}${path40}`, body ? { body } : {});
|
|
39812
39977
|
if (!response.ok) {
|
|
39813
39978
|
const text2 = await response.text();
|
|
39814
|
-
throw new Error(`Membership API Error ${response.status}: ${method} ${
|
|
39979
|
+
throw new Error(`Membership API Error ${response.status}: ${method} ${path40}
|
|
39815
39980
|
${text2}`);
|
|
39816
39981
|
}
|
|
39817
39982
|
const text = await response.text();
|
|
@@ -40004,21 +40169,21 @@ function isAlreadyExistsError(message) {
|
|
|
40004
40169
|
}
|
|
40005
40170
|
function registerTemplateDeployerTools(server2, client) {
|
|
40006
40171
|
const builderClient = WorkflowBuilderClient.fromEnv();
|
|
40007
|
-
const templatesDir =
|
|
40172
|
+
const templatesDir = path28.resolve(__dirname, "..", "templates");
|
|
40008
40173
|
function validateTemplatePath(templateFile) {
|
|
40009
|
-
const resolved2 =
|
|
40010
|
-
const realPath =
|
|
40174
|
+
const resolved2 = path28.resolve(templateFile);
|
|
40175
|
+
const realPath = fs30.existsSync(resolved2) ? fs30.realpathSync(resolved2) : resolved2;
|
|
40011
40176
|
const allowedDirs = [
|
|
40012
40177
|
templatesDir,
|
|
40013
|
-
|
|
40014
|
-
|
|
40015
|
-
].map((d) =>
|
|
40016
|
-
const isAllowed = allowedDirs.some((dir) => realPath.startsWith(dir +
|
|
40178
|
+
path28.resolve(process.cwd(), "templates"),
|
|
40179
|
+
path28.resolve(__dirname, "..", "..", "templates")
|
|
40180
|
+
].map((d) => fs30.existsSync(d) ? fs30.realpathSync(d) : d);
|
|
40181
|
+
const isAllowed = allowedDirs.some((dir) => realPath.startsWith(dir + path28.sep));
|
|
40017
40182
|
if (!isAllowed) {
|
|
40018
40183
|
throw new Error(`Template file must be inside a templates/ directory. Got: ${realPath}`);
|
|
40019
40184
|
}
|
|
40020
|
-
if (
|
|
40021
|
-
const otherMatch = allowedDirs.slice(1).some((d) => !
|
|
40185
|
+
if (path28.relative(allowedDirs[0], realPath).startsWith("..")) {
|
|
40186
|
+
const otherMatch = allowedDirs.slice(1).some((d) => !path28.relative(d, realPath).startsWith(".."));
|
|
40022
40187
|
if (!otherMatch) {
|
|
40023
40188
|
throw new Error(`Template path escapes allowed directory.`);
|
|
40024
40189
|
}
|
|
@@ -40033,26 +40198,26 @@ function registerTemplateDeployerTools(server2, client) {
|
|
|
40033
40198
|
try {
|
|
40034
40199
|
const dirs = [
|
|
40035
40200
|
templatesDir,
|
|
40036
|
-
|
|
40037
|
-
|
|
40201
|
+
path28.resolve(process.cwd(), "templates"),
|
|
40202
|
+
path28.resolve(__dirname, "..", "..", "templates")
|
|
40038
40203
|
];
|
|
40039
40204
|
const templates = [];
|
|
40040
40205
|
const seen = /* @__PURE__ */ new Set();
|
|
40041
40206
|
for (const dir of dirs) {
|
|
40042
|
-
if (!
|
|
40043
|
-
const files =
|
|
40207
|
+
if (!fs30.existsSync(dir)) continue;
|
|
40208
|
+
const files = fs30.readdirSync(dir).filter((f) => f.endsWith(".json"));
|
|
40044
40209
|
for (const file2 of files) {
|
|
40045
|
-
const full =
|
|
40210
|
+
const full = path28.join(dir, file2);
|
|
40046
40211
|
let real;
|
|
40047
40212
|
try {
|
|
40048
|
-
real =
|
|
40213
|
+
real = fs30.realpathSync(full);
|
|
40049
40214
|
} catch {
|
|
40050
40215
|
continue;
|
|
40051
40216
|
}
|
|
40052
40217
|
if (seen.has(real)) continue;
|
|
40053
40218
|
seen.add(real);
|
|
40054
40219
|
try {
|
|
40055
|
-
const content = JSON.parse(
|
|
40220
|
+
const content = JSON.parse(fs30.readFileSync(full, "utf-8"));
|
|
40056
40221
|
if (typeof content.templateName !== "string" || !content.templateName) continue;
|
|
40057
40222
|
const parsed2 = TemplateSchema.safeParse(content);
|
|
40058
40223
|
templates.push({
|
|
@@ -40089,7 +40254,7 @@ function registerTemplateDeployerTools(server2, client) {
|
|
|
40089
40254
|
async ({ templateFile }) => {
|
|
40090
40255
|
try {
|
|
40091
40256
|
const safePath = validateTemplatePath(templateFile);
|
|
40092
|
-
const content = JSON.parse(
|
|
40257
|
+
const content = JSON.parse(fs30.readFileSync(safePath, "utf-8"));
|
|
40093
40258
|
return {
|
|
40094
40259
|
content: [
|
|
40095
40260
|
{
|
|
@@ -40129,7 +40294,7 @@ function registerTemplateDeployerTools(server2, client) {
|
|
|
40129
40294
|
try {
|
|
40130
40295
|
const locId = client.resolveLocationId(locationId2);
|
|
40131
40296
|
const safePath = validateTemplatePath(templateFile);
|
|
40132
|
-
const template = TemplateSchema.parse(JSON.parse(
|
|
40297
|
+
const template = TemplateSchema.parse(JSON.parse(fs30.readFileSync(safePath, "utf-8")));
|
|
40133
40298
|
const resolve12 = (text) => {
|
|
40134
40299
|
if (typeof text !== "string") return text;
|
|
40135
40300
|
let result = text;
|
|
@@ -40501,15 +40666,15 @@ ${errors.join("\n")}` : dryRun ? "\nNo errors!" : "\nNo errors \u2014 every item
|
|
|
40501
40666
|
}
|
|
40502
40667
|
);
|
|
40503
40668
|
}
|
|
40504
|
-
var import_zod62,
|
|
40669
|
+
var import_zod62, fs30, path28, TemplateSchema;
|
|
40505
40670
|
var init_template_deployer = __esm({
|
|
40506
40671
|
"src/tools/template-deployer.ts"() {
|
|
40507
40672
|
"use strict";
|
|
40508
40673
|
import_zod62 = require("zod");
|
|
40509
40674
|
init_workflow_builder_client();
|
|
40510
40675
|
init_tool_helpers();
|
|
40511
|
-
|
|
40512
|
-
|
|
40676
|
+
fs30 = __toESM(require("fs"));
|
|
40677
|
+
path28 = __toESM(require("path"));
|
|
40513
40678
|
init_workflow_builder_client();
|
|
40514
40679
|
TemplateSchema = import_zod62.z.object({
|
|
40515
40680
|
templateName: import_zod62.z.string(),
|
|
@@ -40607,15 +40772,15 @@ async function collectInstallChecks(installedVersion, client, builderClient, reg
|
|
|
40607
40772
|
signal: AbortSignal.timeout(8e3)
|
|
40608
40773
|
});
|
|
40609
40774
|
if (response.status === 401) {
|
|
40610
|
-
return { name: "GHL API key", status: "fail", detail: `401 Unauthorized \u2014 key ${client.
|
|
40775
|
+
return { name: "GHL API key", status: "fail", detail: `401 Unauthorized \u2014 the key ${client.getApiKeyHint()} is invalid or revoked. Run setup_ghl_mcp with a fresh key.` };
|
|
40611
40776
|
}
|
|
40612
40777
|
if (response.status === 403) {
|
|
40613
|
-
return { name: "GHL API key", status: "fail", detail: `403 Forbidden \u2014 key ${client.
|
|
40778
|
+
return { name: "GHL API key", status: "fail", detail: `403 Forbidden \u2014 the key ${client.getApiKeyHint()} doesn't have access to location ${locId}. The key may belong to a different sub-account.` };
|
|
40614
40779
|
}
|
|
40615
40780
|
if (!response.ok) {
|
|
40616
40781
|
return { name: "GHL API key", status: "warn", detail: `HTTP ${response.status} reading /locations/${locId} \u2014 key may be limited or GHL is having issues.` };
|
|
40617
40782
|
}
|
|
40618
|
-
return { name: "GHL API key", status: "pass", detail: `Valid (key ${client.
|
|
40783
|
+
return { name: "GHL API key", status: "pass", detail: `Valid (key ${client.getApiKeyHint()}).` };
|
|
40619
40784
|
} catch (error) {
|
|
40620
40785
|
return { name: "GHL API key", status: "warn", detail: `Network error: ${error instanceof Error ? error.message : String(error)}. Server starts anyway; tool calls will report errors when invoked.` };
|
|
40621
40786
|
}
|
|
@@ -40830,10 +40995,10 @@ async function confirm(run) {
|
|
|
40830
40995
|
async function runProbes(client, loc) {
|
|
40831
40996
|
return Promise.all(
|
|
40832
40997
|
PROBES.map((probe) => confirm(async () => {
|
|
40833
|
-
const
|
|
40998
|
+
const path40 = probe.path.replace("{loc}", loc);
|
|
40834
40999
|
const params = probe.params ? Object.fromEntries(Object.entries(probe.params).map(([k, v]) => [k, substitute(v, loc)])) : void 0;
|
|
40835
41000
|
try {
|
|
40836
|
-
const raw = await client.get(
|
|
41001
|
+
const raw = await client.get(path40, { params, noRetry: true });
|
|
40837
41002
|
const count2 = countRows(raw, probe.listKeys);
|
|
40838
41003
|
return {
|
|
40839
41004
|
key: probe.key,
|
|
@@ -41106,14 +41271,14 @@ var init_checkup = __esm({
|
|
|
41106
41271
|
|
|
41107
41272
|
// src/tools/user-guide.ts
|
|
41108
41273
|
function resolveGuidePath() {
|
|
41109
|
-
const bundled =
|
|
41110
|
-
if (
|
|
41111
|
-
const devRoot =
|
|
41274
|
+
const bundled = path29.resolve(__dirname, "..", "guide", "guide.html");
|
|
41275
|
+
if (fs31.existsSync(bundled)) return bundled;
|
|
41276
|
+
const devRoot = path29.resolve(__dirname, "..", "..");
|
|
41112
41277
|
try {
|
|
41113
|
-
const pkg2 = JSON.parse(
|
|
41278
|
+
const pkg2 = JSON.parse(fs31.readFileSync(path29.join(devRoot, "package.json"), "utf8"));
|
|
41114
41279
|
if (pkg2.name === "@elitedcs/ghl-mcp") {
|
|
41115
|
-
const dev =
|
|
41116
|
-
if (
|
|
41280
|
+
const dev = path29.join(devRoot, "guide", "guide.html");
|
|
41281
|
+
if (fs31.existsSync(dev)) return dev;
|
|
41117
41282
|
}
|
|
41118
41283
|
} catch {
|
|
41119
41284
|
}
|
|
@@ -41122,7 +41287,7 @@ function resolveGuidePath() {
|
|
|
41122
41287
|
function guideContents(file2) {
|
|
41123
41288
|
let html2 = "";
|
|
41124
41289
|
try {
|
|
41125
|
-
html2 =
|
|
41290
|
+
html2 = fs31.readFileSync(file2, "utf8");
|
|
41126
41291
|
} catch {
|
|
41127
41292
|
return "The guide is installed but could not be read.";
|
|
41128
41293
|
}
|
|
@@ -41146,11 +41311,11 @@ function registerUserGuideTools(server2) {
|
|
|
41146
41311
|
{ open: import_zod64.z.boolean().optional().describe("Open the guide in the browser. Takes over the person's screen \u2014 only when they asked for it.") },
|
|
41147
41312
|
async ({ open }) => {
|
|
41148
41313
|
const guidePath = resolveGuidePath();
|
|
41149
|
-
if (!
|
|
41314
|
+
if (!fs31.existsSync(guidePath)) {
|
|
41150
41315
|
return {
|
|
41151
41316
|
content: [{
|
|
41152
41317
|
type: "text",
|
|
41153
|
-
text: `The bundled user guide is missing (expected at ${guidePath}). Installed copies always include it \u2014 a reinstall restores it: npx -y @elitedcs/ghl-mcp cli install. In a development checkout, render it with: node scripts/build-guides.mjs`
|
|
41318
|
+
text: `The bundled user guide is missing (expected at ${guidePath}). Installed copies always include it \u2014 a reinstall restores it: npx -y @elitedcs/ghl-mcp@latest cli install. In a development checkout, render it with: node scripts/build-guides.mjs`
|
|
41154
41319
|
}]
|
|
41155
41320
|
};
|
|
41156
41321
|
}
|
|
@@ -41179,14 +41344,14 @@ ${guidePath}`;
|
|
|
41179
41344
|
}
|
|
41180
41345
|
);
|
|
41181
41346
|
}
|
|
41182
|
-
var import_child_process6, import_zod64,
|
|
41347
|
+
var import_child_process6, import_zod64, fs31, path29;
|
|
41183
41348
|
var init_user_guide = __esm({
|
|
41184
41349
|
"src/tools/user-guide.ts"() {
|
|
41185
41350
|
"use strict";
|
|
41186
41351
|
import_child_process6 = require("child_process");
|
|
41187
41352
|
import_zod64 = require("zod");
|
|
41188
|
-
|
|
41189
|
-
|
|
41353
|
+
fs31 = __toESM(require("fs"));
|
|
41354
|
+
path29 = __toESM(require("path"));
|
|
41190
41355
|
}
|
|
41191
41356
|
});
|
|
41192
41357
|
|
|
@@ -43389,9 +43554,9 @@ async function settle(page) {
|
|
|
43389
43554
|
}
|
|
43390
43555
|
async function readTarget(target) {
|
|
43391
43556
|
if (isUrl(target)) return { html: null, url: target };
|
|
43392
|
-
const abs =
|
|
43393
|
-
if (!
|
|
43394
|
-
return { html:
|
|
43557
|
+
const abs = path30.resolve(target);
|
|
43558
|
+
if (!fs32.existsSync(abs)) throw new Error(`No file at ${abs}.`);
|
|
43559
|
+
return { html: fs32.readFileSync(abs, "utf8"), url: (0, import_node_url.pathToFileURL)(abs).toString() };
|
|
43395
43560
|
}
|
|
43396
43561
|
function slidePixels(index, png, file2, sec, brand) {
|
|
43397
43562
|
let img;
|
|
@@ -43439,7 +43604,7 @@ async function verifyDesign(opts) {
|
|
|
43439
43604
|
const { html: html2, url } = await readTarget(opts.target);
|
|
43440
43605
|
const placement = opts.placement ?? "html";
|
|
43441
43606
|
const medium = opts.medium;
|
|
43442
|
-
|
|
43607
|
+
fs32.mkdirSync(opts.outDir, { recursive: true });
|
|
43443
43608
|
const files = [];
|
|
43444
43609
|
const screenshots = [];
|
|
43445
43610
|
const at = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -43484,8 +43649,8 @@ async function verifyDesign(opts) {
|
|
|
43484
43649
|
try {
|
|
43485
43650
|
await loc.scrollIntoViewIfNeeded({ timeout: 5e3 });
|
|
43486
43651
|
const png = await loc.screenshot({ type: "png", timeout: 15e3 });
|
|
43487
|
-
|
|
43488
|
-
files.push(
|
|
43652
|
+
fs32.writeFileSync(path30.join(opts.outDir, file2), png);
|
|
43653
|
+
files.push(path30.join(opts.outDir, file2));
|
|
43489
43654
|
screenshots.push(file2);
|
|
43490
43655
|
pixels.push(slidePixels(sec.index, png, file2, sec, opts.brand));
|
|
43491
43656
|
} catch {
|
|
@@ -43517,8 +43682,8 @@ async function verifyDesign(opts) {
|
|
|
43517
43682
|
const shot = "page-1440.png";
|
|
43518
43683
|
try {
|
|
43519
43684
|
const png = await page.screenshot({ type: "png", fullPage: true, timeout: 2e4 });
|
|
43520
|
-
|
|
43521
|
-
files.push(
|
|
43685
|
+
fs32.writeFileSync(path30.join(opts.outDir, shot), png);
|
|
43686
|
+
files.push(path30.join(opts.outDir, shot));
|
|
43522
43687
|
screenshots.push(shot);
|
|
43523
43688
|
} catch {
|
|
43524
43689
|
}
|
|
@@ -43526,8 +43691,8 @@ async function verifyDesign(opts) {
|
|
|
43526
43691
|
await page.setViewportSize({ width: 390, height: 844 });
|
|
43527
43692
|
await page.evaluate(SETTLE_SCROLL_JS).catch(() => void 0);
|
|
43528
43693
|
const png = await page.screenshot({ type: "png", fullPage: true, timeout: 2e4 });
|
|
43529
|
-
|
|
43530
|
-
files.push(
|
|
43694
|
+
fs32.writeFileSync(path30.join(opts.outDir, "page-390.png"), png);
|
|
43695
|
+
files.push(path30.join(opts.outDir, "page-390.png"));
|
|
43531
43696
|
screenshots.push("page-390.png");
|
|
43532
43697
|
} catch {
|
|
43533
43698
|
}
|
|
@@ -43600,21 +43765,21 @@ async function verifyDesign(opts) {
|
|
|
43600
43765
|
screenshots
|
|
43601
43766
|
};
|
|
43602
43767
|
const checked = VerificationSchema.parse(report2);
|
|
43603
|
-
const jsonPath =
|
|
43604
|
-
|
|
43768
|
+
const jsonPath = path30.join(opts.outDir, "verification.json");
|
|
43769
|
+
fs32.writeFileSync(jsonPath, JSON.stringify(checked, null, 2) + "\n");
|
|
43605
43770
|
files.push(jsonPath);
|
|
43606
43771
|
const reportHtml = renderVerificationHtml(checked, opts.branding);
|
|
43607
|
-
const htmlPath =
|
|
43608
|
-
|
|
43772
|
+
const htmlPath = path30.join(opts.outDir, "verification.html");
|
|
43773
|
+
fs32.writeFileSync(htmlPath, reportHtml);
|
|
43609
43774
|
files.push(htmlPath);
|
|
43610
43775
|
return { report: checked, files, reportHtml };
|
|
43611
43776
|
}
|
|
43612
|
-
var
|
|
43777
|
+
var fs32, path30, import_node_url, WEB_WIDTHS, SETTLE_SCROLL_JS, FOCUS_JS;
|
|
43613
43778
|
var init_verify = __esm({
|
|
43614
43779
|
"src/command-os/design/verify.ts"() {
|
|
43615
43780
|
"use strict";
|
|
43616
|
-
|
|
43617
|
-
|
|
43781
|
+
fs32 = __toESM(require("node:fs"));
|
|
43782
|
+
path30 = __toESM(require("node:path"));
|
|
43618
43783
|
import_node_url = require("node:url");
|
|
43619
43784
|
init_lint();
|
|
43620
43785
|
init_probe();
|
|
@@ -43819,7 +43984,8 @@ function extractHtml(text) {
|
|
|
43819
43984
|
function claudeGenerate(model = DEFAULT_MODEL, bin = "claude") {
|
|
43820
43985
|
return (instruction, { cwd }) => new Promise((resolveP) => {
|
|
43821
43986
|
const t0 = Date.now();
|
|
43822
|
-
const
|
|
43987
|
+
const cmd = claudeCommand(bin, ["-p", "--output-format", "json", "--max-turns", "1", "--tools", "", "--strict-mcp-config", "--model", model]);
|
|
43988
|
+
const child = (0, import_node_child_process4.spawn)(cmd.command, cmd.args, {
|
|
43823
43989
|
cwd,
|
|
43824
43990
|
env: { ...process.env, CLAUDECODE: "", CLAUDE_CODE_ENTRYPOINT: "" },
|
|
43825
43991
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -43978,6 +44144,7 @@ var init_run = __esm({
|
|
|
43978
44144
|
"src/command-os/design/run.ts"() {
|
|
43979
44145
|
"use strict";
|
|
43980
44146
|
import_node_child_process4 = require("node:child_process");
|
|
44147
|
+
init_claude_locate();
|
|
43981
44148
|
import_node_fs7 = require("node:fs");
|
|
43982
44149
|
import_node_path7 = require("node:path");
|
|
43983
44150
|
init_brief();
|
|
@@ -44031,7 +44198,7 @@ function registerDesignStudioTools(server2) {
|
|
|
44031
44198
|
const { dna, logoSvg, logoPng } = deriveBrandDna(raw, { name, slug: slug3, rights: { declaration: a.rights, at: (/* @__PURE__ */ new Date()).toISOString() }, browser, firecrawl: fc });
|
|
44032
44199
|
if (a.rights === "none" && dna.logo) notes.push("Rights declared as none: the logo is stored for reference only; do not place it on a client-facing page.");
|
|
44033
44200
|
const written = writeBrand(dna, { logoSvg, logoPng, screenshotPng: browser?.screenshotPng });
|
|
44034
|
-
return { ok: true, brand: summarise2(dna), passes: { static: true, browser: Boolean(browser), firecrawl: Boolean(fc) }, spaShell: raw.spaShell, dir: written.dir, files: written.files.map((f) =>
|
|
44201
|
+
return { ok: true, brand: summarise2(dna), passes: { static: true, browser: Boolean(browser), firecrawl: Boolean(fc) }, spaShell: raw.spaShell, dir: written.dir, files: written.files.map((f) => path31.basename(f)), notes };
|
|
44035
44202
|
}
|
|
44036
44203
|
);
|
|
44037
44204
|
safeTool(
|
|
@@ -44198,9 +44365,9 @@ function registerDesignStudioTools(server2) {
|
|
|
44198
44365
|
"Check a written HTML file honours the output contract before grading: section and role markers present and consistent, no outside scripts, one h1 on a page, every image described. Deterministic; no browser. A file that fails this is not graded by verify_design.",
|
|
44199
44366
|
{ file: import_zod67.z.string().describe("Path to the .html file."), medium: import_zod67.z.enum(MEDIA) },
|
|
44200
44367
|
async ({ file: file2, medium }) => {
|
|
44201
|
-
const abs =
|
|
44202
|
-
if (!
|
|
44203
|
-
const r = lintDesignContract(
|
|
44368
|
+
const abs = path31.resolve(file2);
|
|
44369
|
+
if (!fs33.existsSync(abs)) throw new Error(`No file at ${abs}.`);
|
|
44370
|
+
const r = lintDesignContract(fs33.readFileSync(abs, "utf8"), medium);
|
|
44204
44371
|
return { ok: r.ok, problems: r.problems, warnings: r.warnings, facts: r.facts };
|
|
44205
44372
|
}
|
|
44206
44373
|
);
|
|
@@ -44221,7 +44388,7 @@ function registerDesignStudioTools(server2) {
|
|
|
44221
44388
|
async (a) => {
|
|
44222
44389
|
const brand = a.brandSlug ? houseLooks().find((d) => d.slug === a.brandSlug) ?? readBrand(slugify(a.brandSlug)) : void 0;
|
|
44223
44390
|
const isUrl2 = /^https?:\/\//i.test(a.target);
|
|
44224
|
-
const outDir = a.outDir ?? (isUrl2 ?
|
|
44391
|
+
const outDir = a.outDir ?? (isUrl2 ? path31.resolve(`verify-${new URL(a.target).hostname.replace(/[^a-z0-9.-]/gi, "_")}`) : path31.join(path31.dirname(path31.resolve(a.target)), `verify-${path31.basename(a.target, ".html")}`));
|
|
44225
44392
|
const r = await verifyDesign({ target: a.target, medium: a.medium, brand, outDir, placement: a.placement, mode: a.mode, ratio: a.ratio, logo: a.logo, branding: readAgencyBranding() });
|
|
44226
44393
|
const v = r.report;
|
|
44227
44394
|
const failed = v.rules.filter((x) => x.state === "MEASURED-FAIL").map((x) => `${x.ruleId} ${x.name}: ${x.measured ?? ""}`);
|
|
@@ -44236,7 +44403,7 @@ function registerDesignStudioTools(server2) {
|
|
|
44236
44403
|
judgments: v.rules.filter((x) => x.state === "ASSERTED").map((x) => `${x.ruleId} ${x.name}: ${x.detail ?? ""}`),
|
|
44237
44404
|
notApplicable: v.rules.filter((x) => x.state === "N-A").map((x) => `${x.ruleId}: ${x.detail ?? ""}`),
|
|
44238
44405
|
brandMatch: v.brandMatch,
|
|
44239
|
-
report:
|
|
44406
|
+
report: path31.join(outDir, "verification.html"),
|
|
44240
44407
|
files: r.files,
|
|
44241
44408
|
...a.placement === "ghl" && { builderOwned: builderOwnedRules().map((b) => `${b.rule.id} ${b.rule.name}: ${b.why}`) }
|
|
44242
44409
|
};
|
|
@@ -44264,9 +44431,9 @@ function registerDesignStudioTools(server2) {
|
|
|
44264
44431
|
if (!a.pageDataDownloadUrl && !a.file) throw new Error("Pass pageDataDownloadUrl (from get_page_full) or file.");
|
|
44265
44432
|
let page;
|
|
44266
44433
|
if (a.file) {
|
|
44267
|
-
const abs =
|
|
44268
|
-
if (!
|
|
44269
|
-
page = JSON.parse(
|
|
44434
|
+
const abs = path31.resolve(a.file);
|
|
44435
|
+
if (!fs33.existsSync(abs)) throw new Error(`No file at ${abs}.`);
|
|
44436
|
+
page = JSON.parse(fs33.readFileSync(abs, "utf8"));
|
|
44270
44437
|
} else {
|
|
44271
44438
|
const response = await fetch(a.pageDataDownloadUrl, { headers: { Accept: "application/json,*/*" } });
|
|
44272
44439
|
if (!response.ok) throw new Error(`Could not download the page data: HTTP ${response.status}. Re-read get_page_full \u2014 the download URL expires.`);
|
|
@@ -44307,9 +44474,9 @@ function registerDesignStudioTools(server2) {
|
|
|
44307
44474
|
if (!a.file && !a.text) throw new Error("Pass file or text.");
|
|
44308
44475
|
let html2 = a.text ?? "";
|
|
44309
44476
|
if (a.file) {
|
|
44310
|
-
const abs =
|
|
44311
|
-
if (!
|
|
44312
|
-
html2 =
|
|
44477
|
+
const abs = path31.resolve(a.file);
|
|
44478
|
+
if (!fs33.existsSync(abs)) throw new Error(`No file at ${abs}.`);
|
|
44479
|
+
html2 = fs33.readFileSync(abs, "utf8");
|
|
44313
44480
|
}
|
|
44314
44481
|
const r = await gradeCopy(html2, claudeGenerate(a.model), { icp: a.icp });
|
|
44315
44482
|
if (!r.grade) {
|
|
@@ -44344,13 +44511,13 @@ function registerDesignStudioTools(server2) {
|
|
|
44344
44511
|
}
|
|
44345
44512
|
);
|
|
44346
44513
|
}
|
|
44347
|
-
var import_zod67,
|
|
44514
|
+
var import_zod67, fs33, path31, summarise2;
|
|
44348
44515
|
var init_design_studio = __esm({
|
|
44349
44516
|
"src/tools/design-studio.ts"() {
|
|
44350
44517
|
"use strict";
|
|
44351
44518
|
import_zod67 = require("zod");
|
|
44352
|
-
|
|
44353
|
-
|
|
44519
|
+
fs33 = __toESM(require("node:fs"));
|
|
44520
|
+
path31 = __toESM(require("node:path"));
|
|
44354
44521
|
init_tool_helpers();
|
|
44355
44522
|
init_extract();
|
|
44356
44523
|
init_extract_browser();
|
|
@@ -44387,11 +44554,11 @@ var init_design_studio = __esm({
|
|
|
44387
44554
|
|
|
44388
44555
|
// src/command-os/connectors/store.ts
|
|
44389
44556
|
function connectorsPath() {
|
|
44390
|
-
return
|
|
44557
|
+
return path32.join(appDataDir(), "connectors.json");
|
|
44391
44558
|
}
|
|
44392
44559
|
function readFile() {
|
|
44393
44560
|
try {
|
|
44394
|
-
const raw =
|
|
44561
|
+
const raw = fs34.readFileSync(connectorsPath(), "utf-8");
|
|
44395
44562
|
const parsed2 = JSON.parse(raw);
|
|
44396
44563
|
if (!parsed2 || typeof parsed2 !== "object" || !parsed2.connectors) return { v: 1, connectors: {} };
|
|
44397
44564
|
return parsed2;
|
|
@@ -44403,7 +44570,7 @@ function readForWrite() {
|
|
|
44403
44570
|
const target = connectorsPath();
|
|
44404
44571
|
let raw;
|
|
44405
44572
|
try {
|
|
44406
|
-
raw =
|
|
44573
|
+
raw = fs34.readFileSync(target, "utf-8");
|
|
44407
44574
|
} catch (e) {
|
|
44408
44575
|
if (e?.code === "ENOENT") return { v: 1, connectors: {} };
|
|
44409
44576
|
throw new Error("Your connector file could not be read, so nothing was changed. Nothing was lost.");
|
|
@@ -44415,8 +44582,8 @@ function readForWrite() {
|
|
|
44415
44582
|
} catch {
|
|
44416
44583
|
const kept = `${target}.unreadable-${Date.now()}`;
|
|
44417
44584
|
try {
|
|
44418
|
-
|
|
44419
|
-
|
|
44585
|
+
fs34.copyFileSync(target, kept);
|
|
44586
|
+
fs34.chmodSync(kept, 384);
|
|
44420
44587
|
} catch {
|
|
44421
44588
|
}
|
|
44422
44589
|
throw new Error(
|
|
@@ -44430,23 +44597,23 @@ function writeFile(f) {
|
|
|
44430
44597
|
const tmp = `${target}.${process.pid}.tmp`;
|
|
44431
44598
|
let renamed = false;
|
|
44432
44599
|
try {
|
|
44433
|
-
|
|
44600
|
+
fs34.writeFileSync(tmp, JSON.stringify(f, null, 2) + "\n", { encoding: "utf-8", mode: 384 });
|
|
44434
44601
|
try {
|
|
44435
|
-
|
|
44602
|
+
fs34.chmodSync(tmp, 384);
|
|
44436
44603
|
} catch {
|
|
44437
44604
|
}
|
|
44438
|
-
|
|
44605
|
+
fs34.renameSync(tmp, target);
|
|
44439
44606
|
renamed = true;
|
|
44440
44607
|
} finally {
|
|
44441
44608
|
if (!renamed) {
|
|
44442
44609
|
try {
|
|
44443
|
-
|
|
44610
|
+
fs34.unlinkSync(tmp);
|
|
44444
44611
|
} catch {
|
|
44445
44612
|
}
|
|
44446
44613
|
}
|
|
44447
44614
|
}
|
|
44448
44615
|
try {
|
|
44449
|
-
|
|
44616
|
+
fs34.chmodSync(target, 384);
|
|
44450
44617
|
} catch {
|
|
44451
44618
|
}
|
|
44452
44619
|
}
|
|
@@ -44480,12 +44647,12 @@ async function withKey(system, fn) {
|
|
|
44480
44647
|
if (!s) throw new Error(`${system} is not connected on this machine.`);
|
|
44481
44648
|
return fn(s.key);
|
|
44482
44649
|
}
|
|
44483
|
-
var
|
|
44650
|
+
var fs34, path32, hintOf;
|
|
44484
44651
|
var init_store3 = __esm({
|
|
44485
44652
|
"src/command-os/connectors/store.ts"() {
|
|
44486
44653
|
"use strict";
|
|
44487
|
-
|
|
44488
|
-
|
|
44654
|
+
fs34 = __toESM(require("node:fs"));
|
|
44655
|
+
path32 = __toESM(require("node:path"));
|
|
44489
44656
|
init_credentials_store();
|
|
44490
44657
|
hintOf = (key) => `\u2026${String(key).slice(-4)}`;
|
|
44491
44658
|
}
|
|
@@ -45141,8 +45308,8 @@ var init_to_engagements = __esm({
|
|
|
45141
45308
|
|
|
45142
45309
|
// src/tools/connectors.ts
|
|
45143
45310
|
function stripeFetcher(key) {
|
|
45144
|
-
return async (
|
|
45145
|
-
const url = new URL(`https://api.stripe.com${
|
|
45311
|
+
return async (path40, query) => {
|
|
45312
|
+
const url = new URL(`https://api.stripe.com${path40}`);
|
|
45146
45313
|
for (const [k, v] of Object.entries(query ?? {})) url.searchParams.set(k, v);
|
|
45147
45314
|
const res = await fetch(url, { headers: { Authorization: `Bearer ${key}` } });
|
|
45148
45315
|
if (!res.ok) {
|
|
@@ -45151,13 +45318,13 @@ function stripeFetcher(key) {
|
|
|
45151
45318
|
return res.json();
|
|
45152
45319
|
};
|
|
45153
45320
|
}
|
|
45154
|
-
async function listAll(f,
|
|
45321
|
+
async function listAll(f, path40, extra = {}) {
|
|
45155
45322
|
const out = [];
|
|
45156
45323
|
let after;
|
|
45157
45324
|
for (let page = 0; page < 20; page++) {
|
|
45158
45325
|
const q2 = { limit: "100", ...extra };
|
|
45159
45326
|
if (after) q2.starting_after = after;
|
|
45160
|
-
const res = await f(
|
|
45327
|
+
const res = await f(path40, q2);
|
|
45161
45328
|
const data = res?.data ?? [];
|
|
45162
45329
|
out.push(...data);
|
|
45163
45330
|
if (!res?.has_more || !data.length) break;
|
|
@@ -45430,7 +45597,7 @@ var init_connectors = __esm({
|
|
|
45430
45597
|
// src/command-os/connectors/google-calendar-store.ts
|
|
45431
45598
|
function read() {
|
|
45432
45599
|
try {
|
|
45433
|
-
return SecretSchema.parse(JSON.parse(
|
|
45600
|
+
return SecretSchema.parse(JSON.parse(fs35.readFileSync(googleCalendarPath(), "utf8")));
|
|
45434
45601
|
} catch (error) {
|
|
45435
45602
|
if (error.code === "ENOENT") return null;
|
|
45436
45603
|
throw new Error("The Google Calendar sign-in on this machine could not be read. Connect your Google Calendar again.");
|
|
@@ -45453,16 +45620,16 @@ function saveCalendarSecret(secret) {
|
|
|
45453
45620
|
try {
|
|
45454
45621
|
const validated = SecretSchema.parse(secret);
|
|
45455
45622
|
const dir = ensureAppDataDir();
|
|
45456
|
-
if (process.platform !== "win32")
|
|
45623
|
+
if (process.platform !== "win32") fs35.chmodSync(dir, 448);
|
|
45457
45624
|
tmp = `${googleCalendarPath()}.${(0, import_node_crypto9.randomUUID)()}.tmp`;
|
|
45458
|
-
|
|
45459
|
-
|
|
45625
|
+
fs35.writeFileSync(tmp, JSON.stringify(validated) + "\n", { mode: 384, flag: "wx" });
|
|
45626
|
+
fs35.renameSync(tmp, googleCalendarPath());
|
|
45460
45627
|
} catch {
|
|
45461
45628
|
throw new Error("The Google Calendar sign-in could not be saved privately on this machine. Nothing new was saved.");
|
|
45462
45629
|
} finally {
|
|
45463
45630
|
if (tmp) {
|
|
45464
45631
|
try {
|
|
45465
|
-
|
|
45632
|
+
fs35.unlinkSync(tmp);
|
|
45466
45633
|
} catch {
|
|
45467
45634
|
}
|
|
45468
45635
|
}
|
|
@@ -45470,19 +45637,19 @@ function saveCalendarSecret(secret) {
|
|
|
45470
45637
|
}
|
|
45471
45638
|
function removeCalendarSecret() {
|
|
45472
45639
|
try {
|
|
45473
|
-
|
|
45640
|
+
fs35.unlinkSync(googleCalendarPath());
|
|
45474
45641
|
return true;
|
|
45475
45642
|
} catch (error) {
|
|
45476
45643
|
if (error.code === "ENOENT") return false;
|
|
45477
45644
|
throw new Error("The Google Calendar sign-in could not be removed from this machine.");
|
|
45478
45645
|
}
|
|
45479
45646
|
}
|
|
45480
|
-
var
|
|
45647
|
+
var fs35, path33, import_node_crypto9, import_zod69, CALENDAR_SCOPE, SecretSchema, googleCalendarPath;
|
|
45481
45648
|
var init_google_calendar_store = __esm({
|
|
45482
45649
|
"src/command-os/connectors/google-calendar-store.ts"() {
|
|
45483
45650
|
"use strict";
|
|
45484
|
-
|
|
45485
|
-
|
|
45651
|
+
fs35 = __toESM(require("node:fs"));
|
|
45652
|
+
path33 = __toESM(require("node:path"));
|
|
45486
45653
|
import_node_crypto9 = require("node:crypto");
|
|
45487
45654
|
import_zod69 = require("zod");
|
|
45488
45655
|
init_credentials_store();
|
|
@@ -45494,7 +45661,7 @@ var init_google_calendar_store = __esm({
|
|
|
45494
45661
|
scope: import_zod69.z.literal(CALENDAR_SCOPE),
|
|
45495
45662
|
connectedAt: import_zod69.z.string()
|
|
45496
45663
|
});
|
|
45497
|
-
googleCalendarPath = () =>
|
|
45664
|
+
googleCalendarPath = () => path33.join(appDataDir(), "google-calendar.json");
|
|
45498
45665
|
}
|
|
45499
45666
|
});
|
|
45500
45667
|
|
|
@@ -45596,16 +45763,16 @@ function createGoogleCalendar(deps = {}) {
|
|
|
45596
45763
|
requireOnlyCalendar(result.scope);
|
|
45597
45764
|
return result.access_token;
|
|
45598
45765
|
}
|
|
45599
|
-
async function pages(
|
|
45766
|
+
async function pages(path40, access, item, query = {}) {
|
|
45600
45767
|
const result = [];
|
|
45601
45768
|
const seen = /* @__PURE__ */ new Set();
|
|
45602
45769
|
let pageToken;
|
|
45603
45770
|
for (let page = 0; page < 100; page++) {
|
|
45604
|
-
const url = new URL(`${API}${
|
|
45771
|
+
const url = new URL(`${API}${path40}`);
|
|
45605
45772
|
for (const [k, v] of Object.entries(query)) url.searchParams.set(k, v);
|
|
45606
45773
|
if (pageToken) url.searchParams.set("pageToken", pageToken);
|
|
45607
45774
|
const data = parsed(
|
|
45608
|
-
import_zod70.z.object({ items: import_zod70.z.array(item).optional(), nextPageToken: import_zod70.z.string().optional(), kind: import_zod70.z.string().optional() }).refine((p) => p.items !== void 0 || p.kind === (
|
|
45775
|
+
import_zod70.z.object({ items: import_zod70.z.array(item).optional(), nextPageToken: import_zod70.z.string().optional(), kind: import_zod70.z.string().optional() }).refine((p) => p.items !== void 0 || p.kind === (path40.endsWith("/events") ? "calendar#events" : "calendar#calendarList")),
|
|
45609
45776
|
await request(url.href, { headers: { Authorization: `Bearer ${access}` } })
|
|
45610
45777
|
);
|
|
45611
45778
|
result.push(...data.items ?? []);
|
|
@@ -45907,10 +46074,10 @@ function score(answers, available) {
|
|
|
45907
46074
|
}
|
|
45908
46075
|
var hours = minutes * times / 60;
|
|
45909
46076
|
if (hours <= 0) return;
|
|
45910
|
-
var
|
|
46077
|
+
var runnable2 = t.tools.filter(function(n) {
|
|
45911
46078
|
return known ? have[n] === true : true;
|
|
45912
46079
|
});
|
|
45913
|
-
var usable = t.tools.length > 0 &&
|
|
46080
|
+
var usable = t.tools.length > 0 && runnable2.length === t.tools.length;
|
|
45914
46081
|
scored.push({
|
|
45915
46082
|
task: t.id,
|
|
45916
46083
|
label: t.label,
|
|
@@ -46012,11 +46179,11 @@ var init_audit = __esm({
|
|
|
46012
46179
|
// src/command-os/delivery-audit/store.ts
|
|
46013
46180
|
function deliveryAuditPath() {
|
|
46014
46181
|
const override = process.env[DELIVERY_AUDIT_ENV];
|
|
46015
|
-
return override && override.trim() ? override :
|
|
46182
|
+
return override && override.trim() ? override : path34.join(appDataDir(), "delivery-audit.json");
|
|
46016
46183
|
}
|
|
46017
46184
|
function readDeliveryAudit() {
|
|
46018
46185
|
try {
|
|
46019
|
-
const parsed2 = JSON.parse(
|
|
46186
|
+
const parsed2 = JSON.parse(fs36.readFileSync(deliveryAuditPath(), "utf-8"));
|
|
46020
46187
|
if (!parsed2 || typeof parsed2 !== "object" || !parsed2.answers || typeof parsed2.answers !== "object") return { ...EMPTY3 };
|
|
46021
46188
|
return { v: 1, answers: parsed2.answers, updated_at: parsed2.updated_at };
|
|
46022
46189
|
} catch {
|
|
@@ -46027,7 +46194,7 @@ function readForWrite2() {
|
|
|
46027
46194
|
const target = deliveryAuditPath();
|
|
46028
46195
|
let raw;
|
|
46029
46196
|
try {
|
|
46030
|
-
raw =
|
|
46197
|
+
raw = fs36.readFileSync(target, "utf-8");
|
|
46031
46198
|
} catch (e) {
|
|
46032
46199
|
if (e?.code === "ENOENT") return { ...EMPTY3 };
|
|
46033
46200
|
throw new Error("Your saved audit could not be read, so nothing was changed. Nothing was lost.");
|
|
@@ -46039,7 +46206,7 @@ function readForWrite2() {
|
|
|
46039
46206
|
} catch {
|
|
46040
46207
|
const kept = `${target}.unreadable-${Date.now()}`;
|
|
46041
46208
|
try {
|
|
46042
|
-
|
|
46209
|
+
fs36.copyFileSync(target, kept);
|
|
46043
46210
|
} catch {
|
|
46044
46211
|
}
|
|
46045
46212
|
throw new Error(`Your saved audit is damaged, so nothing was written over it. A copy is at ${kept}. Start again with reset:true when you are ready.`);
|
|
@@ -46051,13 +46218,13 @@ function writeFile2(f) {
|
|
|
46051
46218
|
const tmp = `${target}.${process.pid}.tmp`;
|
|
46052
46219
|
let renamed = false;
|
|
46053
46220
|
try {
|
|
46054
|
-
|
|
46055
|
-
|
|
46221
|
+
fs36.writeFileSync(tmp, JSON.stringify(f, null, 2) + "\n", { encoding: "utf-8", mode: 384 });
|
|
46222
|
+
fs36.renameSync(tmp, target);
|
|
46056
46223
|
renamed = true;
|
|
46057
46224
|
} finally {
|
|
46058
46225
|
if (!renamed) {
|
|
46059
46226
|
try {
|
|
46060
|
-
|
|
46227
|
+
fs36.unlinkSync(tmp);
|
|
46061
46228
|
} catch {
|
|
46062
46229
|
}
|
|
46063
46230
|
}
|
|
@@ -46078,12 +46245,12 @@ function resetDeliveryAudit(now = () => /* @__PURE__ */ new Date()) {
|
|
|
46078
46245
|
writeFile2(next);
|
|
46079
46246
|
return next;
|
|
46080
46247
|
}
|
|
46081
|
-
var
|
|
46248
|
+
var fs36, path34, EMPTY3, DELIVERY_AUDIT_ENV;
|
|
46082
46249
|
var init_store4 = __esm({
|
|
46083
46250
|
"src/command-os/delivery-audit/store.ts"() {
|
|
46084
46251
|
"use strict";
|
|
46085
|
-
|
|
46086
|
-
|
|
46252
|
+
fs36 = __toESM(require("node:fs"));
|
|
46253
|
+
path34 = __toESM(require("node:path"));
|
|
46087
46254
|
init_credentials_store();
|
|
46088
46255
|
EMPTY3 = { v: 1, answers: {} };
|
|
46089
46256
|
DELIVERY_AUDIT_ENV = "GHL_MCP_DELIVERY_AUDIT";
|
|
@@ -46398,7 +46565,7 @@ var init_automation = __esm({
|
|
|
46398
46565
|
// src/command-os/build/store.ts
|
|
46399
46566
|
function automationsPath() {
|
|
46400
46567
|
const override = process.env[AUTOMATIONS_ENV];
|
|
46401
|
-
return override && override.trim() ? override :
|
|
46568
|
+
return override && override.trim() ? override : path35.join(appDataDir(), "automations.json");
|
|
46402
46569
|
}
|
|
46403
46570
|
function parse4(raw) {
|
|
46404
46571
|
const p = JSON.parse(raw);
|
|
@@ -46407,7 +46574,7 @@ function parse4(raw) {
|
|
|
46407
46574
|
}
|
|
46408
46575
|
function readAutomations() {
|
|
46409
46576
|
try {
|
|
46410
|
-
return parse4(
|
|
46577
|
+
return parse4(fs37.readFileSync(automationsPath(), "utf-8")).builds;
|
|
46411
46578
|
} catch {
|
|
46412
46579
|
return { ...EMPTY4.builds };
|
|
46413
46580
|
}
|
|
@@ -46416,7 +46583,7 @@ function readForWrite3() {
|
|
|
46416
46583
|
const target = automationsPath();
|
|
46417
46584
|
let raw;
|
|
46418
46585
|
try {
|
|
46419
|
-
raw =
|
|
46586
|
+
raw = fs37.readFileSync(target, "utf-8");
|
|
46420
46587
|
} catch (e) {
|
|
46421
46588
|
if (e?.code === "ENOENT") return { v: 1, builds: {} };
|
|
46422
46589
|
throw new Error("Your builds file could not be read, so nothing was changed. Nothing was lost.");
|
|
@@ -46426,7 +46593,7 @@ function readForWrite3() {
|
|
|
46426
46593
|
} catch {
|
|
46427
46594
|
const kept = `${target}.unreadable-${Date.now()}`;
|
|
46428
46595
|
try {
|
|
46429
|
-
|
|
46596
|
+
fs37.copyFileSync(target, kept);
|
|
46430
46597
|
} catch {
|
|
46431
46598
|
}
|
|
46432
46599
|
throw new Error(
|
|
@@ -46440,13 +46607,13 @@ function writeFile3(f) {
|
|
|
46440
46607
|
const tmp = `${target}.${process.pid}.tmp`;
|
|
46441
46608
|
let renamed = false;
|
|
46442
46609
|
try {
|
|
46443
|
-
|
|
46444
|
-
|
|
46610
|
+
fs37.writeFileSync(tmp, JSON.stringify(f, null, 2) + "\n", { encoding: "utf-8", mode: 384 });
|
|
46611
|
+
fs37.renameSync(tmp, target);
|
|
46445
46612
|
renamed = true;
|
|
46446
46613
|
} finally {
|
|
46447
46614
|
if (!renamed) {
|
|
46448
46615
|
try {
|
|
46449
|
-
|
|
46616
|
+
fs37.unlinkSync(tmp);
|
|
46450
46617
|
} catch {
|
|
46451
46618
|
}
|
|
46452
46619
|
}
|
|
@@ -46472,12 +46639,12 @@ function newAutomationId(now = () => /* @__PURE__ */ new Date()) {
|
|
|
46472
46639
|
}
|
|
46473
46640
|
return id;
|
|
46474
46641
|
}
|
|
46475
|
-
var
|
|
46642
|
+
var fs37, path35, AUTOMATIONS_ENV, EMPTY4;
|
|
46476
46643
|
var init_store5 = __esm({
|
|
46477
46644
|
"src/command-os/build/store.ts"() {
|
|
46478
46645
|
"use strict";
|
|
46479
|
-
|
|
46480
|
-
|
|
46646
|
+
fs37 = __toESM(require("node:fs"));
|
|
46647
|
+
path35 = __toESM(require("node:path"));
|
|
46481
46648
|
init_credentials_store();
|
|
46482
46649
|
AUTOMATIONS_ENV = "GHL_MCP_AUTOMATIONS";
|
|
46483
46650
|
EMPTY4 = { v: 1, builds: {} };
|
|
@@ -46773,7 +46940,7 @@ var init_client_week = __esm({
|
|
|
46773
46940
|
// src/board-roster.ts
|
|
46774
46941
|
function readBoardState() {
|
|
46775
46942
|
try {
|
|
46776
|
-
const raw = JSON.parse(
|
|
46943
|
+
const raw = JSON.parse(fs38.readFileSync(path36.join(appDataDir(), "cockpit-state.json"), "utf8"));
|
|
46777
46944
|
if (raw && typeof raw === "object" && raw.clients && typeof raw.clients === "object") return raw;
|
|
46778
46945
|
} catch {
|
|
46779
46946
|
}
|
|
@@ -46791,12 +46958,12 @@ function knownClients(registry2, state) {
|
|
|
46791
46958
|
}
|
|
46792
46959
|
return [...out.values()];
|
|
46793
46960
|
}
|
|
46794
|
-
var
|
|
46961
|
+
var fs38, path36;
|
|
46795
46962
|
var init_board_roster = __esm({
|
|
46796
46963
|
"src/board-roster.ts"() {
|
|
46797
46964
|
"use strict";
|
|
46798
|
-
|
|
46799
|
-
|
|
46965
|
+
fs38 = __toESM(require("fs"));
|
|
46966
|
+
path36 = __toESM(require("path"));
|
|
46800
46967
|
init_credentials_store();
|
|
46801
46968
|
init_client_engagements();
|
|
46802
46969
|
}
|
|
@@ -48831,9 +48998,9 @@ function presetForBusinessType(type) {
|
|
|
48831
48998
|
return "generic";
|
|
48832
48999
|
}
|
|
48833
49000
|
}
|
|
48834
|
-
function setPath(target,
|
|
49001
|
+
function setPath(target, path40, value) {
|
|
48835
49002
|
if (value === void 0) return;
|
|
48836
|
-
const parts =
|
|
49003
|
+
const parts = path40.split(".");
|
|
48837
49004
|
let node = target;
|
|
48838
49005
|
for (let i = 0; i < parts.length - 1; i++) {
|
|
48839
49006
|
const k = parts[i];
|
|
@@ -49904,17 +50071,17 @@ function planFromPreset(input) {
|
|
|
49904
50071
|
}
|
|
49905
50072
|
function presetsRoot() {
|
|
49906
50073
|
const candidates = [
|
|
49907
|
-
|
|
50074
|
+
path37.join(__dirname, "..", "skills", "blueprint", "presets"),
|
|
49908
50075
|
// dist/ → package root
|
|
49909
|
-
|
|
50076
|
+
path37.join(__dirname, "..", "..", "skills", "blueprint", "presets"),
|
|
49910
50077
|
// src/intake-to-build in dev
|
|
49911
|
-
|
|
50078
|
+
path37.join(process.cwd(), "skills", "blueprint", "presets")
|
|
49912
50079
|
];
|
|
49913
|
-
return candidates.find((c) =>
|
|
50080
|
+
return candidates.find((c) => fs39.existsSync(c)) ?? null;
|
|
49914
50081
|
}
|
|
49915
50082
|
function readPresetFile(dir, base) {
|
|
49916
50083
|
try {
|
|
49917
|
-
const raw = JSON.parse(
|
|
50084
|
+
const raw = JSON.parse(fs39.readFileSync(path37.join(dir, `${base}.preset.json`), "utf8"));
|
|
49918
50085
|
if (raw && typeof raw === "object" && typeof raw.presetId === "string" && raw.skeleton) return raw;
|
|
49919
50086
|
return null;
|
|
49920
50087
|
} catch {
|
|
@@ -49926,7 +50093,7 @@ function loadPresetChoice(requested, briefPreset) {
|
|
|
49926
50093
|
if (!dir) {
|
|
49927
50094
|
return { ok: false, error: "The preset library is not installed with this copy of the tool (skills/blueprint/presets is missing) \u2014 reinstall, or author the plan by hand." };
|
|
49928
50095
|
}
|
|
49929
|
-
const bases =
|
|
50096
|
+
const bases = fs39.readdirSync(dir).filter((f) => f.endsWith(".preset.json")).map((f) => f.replace(/\.preset\.json$/, ""));
|
|
49930
50097
|
const tryMatch = (want) => {
|
|
49931
50098
|
const norm6 = slugify2(want);
|
|
49932
50099
|
for (const base of bases) {
|
|
@@ -49957,12 +50124,12 @@ function loadPresetChoice(requested, briefPreset) {
|
|
|
49957
50124
|
if (generic) return { ok: true, preset: generic, file: "generic-client.preset.json", note: "Using the generic preset." };
|
|
49958
50125
|
return { ok: false, error: "No usable preset file was found in the library." };
|
|
49959
50126
|
}
|
|
49960
|
-
var
|
|
50127
|
+
var fs39, path37, PRESET_TOKEN_ROOTS, FLAG_ALIASES, INLINE_TOKEN_RE, ADMIN_ROLE_RE, BRIEF_CALENDAR_TYPE_MAP;
|
|
49961
50128
|
var init_plan_from_preset = __esm({
|
|
49962
50129
|
"src/intake-to-build/plan-from-preset.ts"() {
|
|
49963
50130
|
"use strict";
|
|
49964
|
-
|
|
49965
|
-
|
|
50131
|
+
fs39 = __toESM(require("fs"));
|
|
50132
|
+
path37 = __toESM(require("path"));
|
|
49966
50133
|
init_plan();
|
|
49967
50134
|
init_brief2();
|
|
49968
50135
|
init_normalizer();
|
|
@@ -51186,13 +51353,13 @@ ${text2}`);
|
|
|
51186
51353
|
};
|
|
51187
51354
|
}
|
|
51188
51355
|
function makePipelineApi(builderClient) {
|
|
51189
|
-
return async (method,
|
|
51190
|
-
const url = `https://backend.leadconnectorhq.com/opportunities/pipelines${
|
|
51356
|
+
return async (method, path40, body) => {
|
|
51357
|
+
const url = `https://backend.leadconnectorhq.com/opportunities/pipelines${path40}`;
|
|
51191
51358
|
const sendsBody = body !== void 0 && (method === "POST" || method === "PUT");
|
|
51192
51359
|
const response = await builderRequest(builderClient, method, url, sendsBody ? { body } : {});
|
|
51193
51360
|
if (!response.ok) {
|
|
51194
51361
|
const text2 = await response.text();
|
|
51195
|
-
throw new Error(`Pipeline API ${response.status}: ${method} ${
|
|
51362
|
+
throw new Error(`Pipeline API ${response.status}: ${method} ${path40}
|
|
51196
51363
|
${text2}`);
|
|
51197
51364
|
}
|
|
51198
51365
|
const text = await response.text();
|
|
@@ -51205,8 +51372,8 @@ ${text2}`);
|
|
|
51205
51372
|
};
|
|
51206
51373
|
}
|
|
51207
51374
|
function makeFunnelApi(builderClient) {
|
|
51208
|
-
return async (method,
|
|
51209
|
-
const url = `https://backend.leadconnectorhq.com/funnels${
|
|
51375
|
+
return async (method, path40, body) => {
|
|
51376
|
+
const url = `https://backend.leadconnectorhq.com/funnels${path40}`;
|
|
51210
51377
|
const sendsBody = body !== void 0 && (method === "POST" || method === "PUT");
|
|
51211
51378
|
const response = await builderRequest(builderClient, method, url, {
|
|
51212
51379
|
...sendsBody ? { body } : {},
|
|
@@ -51219,7 +51386,7 @@ function makeFunnelApi(builderClient) {
|
|
|
51219
51386
|
});
|
|
51220
51387
|
if (!response.ok) {
|
|
51221
51388
|
const text2 = await response.text();
|
|
51222
|
-
throw new Error(`Funnel API ${response.status}: ${method} ${
|
|
51389
|
+
throw new Error(`Funnel API ${response.status}: ${method} ${path40}
|
|
51223
51390
|
${text2.slice(0, 300)}`);
|
|
51224
51391
|
}
|
|
51225
51392
|
const text = await response.text();
|
|
@@ -52814,8 +52981,8 @@ __export(index_exports, {
|
|
|
52814
52981
|
});
|
|
52815
52982
|
module.exports = __toCommonJS(index_exports);
|
|
52816
52983
|
var dotenv2 = __toESM(require("dotenv"));
|
|
52817
|
-
var
|
|
52818
|
-
var
|
|
52984
|
+
var path39 = __toESM(require("path"));
|
|
52985
|
+
var fs41 = __toESM(require("fs"));
|
|
52819
52986
|
var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
52820
52987
|
|
|
52821
52988
|
// src/version-compare.ts
|
|
@@ -52887,9 +53054,9 @@ var import_zod82 = require("zod");
|
|
|
52887
53054
|
|
|
52888
53055
|
// src/skill-installer.ts
|
|
52889
53056
|
var import_node_crypto12 = require("node:crypto");
|
|
52890
|
-
var
|
|
52891
|
-
var
|
|
52892
|
-
var
|
|
53057
|
+
var fs40 = __toESM(require("node:fs"));
|
|
53058
|
+
var path38 = __toESM(require("node:path"));
|
|
53059
|
+
var os7 = __toESM(require("node:os"));
|
|
52893
53060
|
function sha2562(buf) {
|
|
52894
53061
|
return (0, import_node_crypto12.createHash)("sha256").update(buf).digest("hex");
|
|
52895
53062
|
}
|
|
@@ -52903,9 +53070,9 @@ function isOlderRelease(candidate, installed) {
|
|
|
52903
53070
|
return false;
|
|
52904
53071
|
}
|
|
52905
53072
|
function bundledSkillsDir(baseDir) {
|
|
52906
|
-
const candidate =
|
|
53073
|
+
const candidate = path38.resolve(baseDir, "..", "skills");
|
|
52907
53074
|
try {
|
|
52908
|
-
return
|
|
53075
|
+
return fs40.statSync(candidate).isDirectory() ? candidate : null;
|
|
52909
53076
|
} catch {
|
|
52910
53077
|
return null;
|
|
52911
53078
|
}
|
|
@@ -52913,10 +53080,10 @@ function bundledSkillsDir(baseDir) {
|
|
|
52913
53080
|
function listFiles(root) {
|
|
52914
53081
|
const out = [];
|
|
52915
53082
|
const walk = (dir) => {
|
|
52916
|
-
for (const entry of
|
|
52917
|
-
const full =
|
|
53083
|
+
for (const entry of fs40.readdirSync(dir, { withFileTypes: true })) {
|
|
53084
|
+
const full = path38.join(dir, entry.name);
|
|
52918
53085
|
if (entry.isDirectory()) walk(full);
|
|
52919
|
-
else if (entry.isFile()) out.push(
|
|
53086
|
+
else if (entry.isFile()) out.push(path38.relative(root, full));
|
|
52920
53087
|
}
|
|
52921
53088
|
};
|
|
52922
53089
|
walk(root);
|
|
@@ -52924,7 +53091,7 @@ function listFiles(root) {
|
|
|
52924
53091
|
}
|
|
52925
53092
|
function readMarker(markerPath) {
|
|
52926
53093
|
try {
|
|
52927
|
-
const parsed2 = JSON.parse(
|
|
53094
|
+
const parsed2 = JSON.parse(fs40.readFileSync(markerPath, "utf8"));
|
|
52928
53095
|
return parsed2 && typeof parsed2 === "object" && parsed2.files ? parsed2 : null;
|
|
52929
53096
|
} catch {
|
|
52930
53097
|
return null;
|
|
@@ -52938,35 +53105,35 @@ function installBundledSkills(opts) {
|
|
|
52938
53105
|
skippedOlderPackage: [],
|
|
52939
53106
|
unchanged: [],
|
|
52940
53107
|
errors: [],
|
|
52941
|
-
targetDir: opts.targetDir ??
|
|
53108
|
+
targetDir: opts.targetDir ?? path38.join(os7.homedir(), ".claude", "skills"),
|
|
52942
53109
|
bundledDir: null
|
|
52943
53110
|
};
|
|
52944
53111
|
const bundled = bundledSkillsDir(opts.baseDir);
|
|
52945
53112
|
result.bundledDir = bundled;
|
|
52946
53113
|
if (!bundled) return result;
|
|
52947
53114
|
try {
|
|
52948
|
-
|
|
53115
|
+
fs40.mkdirSync(result.targetDir, { recursive: true });
|
|
52949
53116
|
} catch (e) {
|
|
52950
53117
|
result.errors.push(`cannot create ${result.targetDir}: ${String(e)}`);
|
|
52951
53118
|
return result;
|
|
52952
53119
|
}
|
|
52953
|
-
const markerPath =
|
|
53120
|
+
const markerPath = path38.join(result.targetDir, ".ghl-command-skills.json");
|
|
52954
53121
|
const marker = readMarker(markerPath) ?? { packageVersion: "", files: {} };
|
|
52955
53122
|
const newMarker = { packageVersion: opts.packageVersion, files: { ...marker.files } };
|
|
52956
53123
|
for (const rel of listFiles(bundled)) {
|
|
52957
53124
|
try {
|
|
52958
|
-
const src =
|
|
52959
|
-
const dest =
|
|
53125
|
+
const src = fs40.readFileSync(path38.join(bundled, rel));
|
|
53126
|
+
const dest = path38.join(result.targetDir, rel);
|
|
52960
53127
|
const srcHash = sha2562(src);
|
|
52961
53128
|
let destBuf = null;
|
|
52962
53129
|
try {
|
|
52963
|
-
destBuf =
|
|
53130
|
+
destBuf = fs40.readFileSync(dest);
|
|
52964
53131
|
} catch {
|
|
52965
53132
|
destBuf = null;
|
|
52966
53133
|
}
|
|
52967
53134
|
if (destBuf === null) {
|
|
52968
|
-
|
|
52969
|
-
|
|
53135
|
+
fs40.mkdirSync(path38.dirname(dest), { recursive: true });
|
|
53136
|
+
fs40.writeFileSync(dest, src);
|
|
52970
53137
|
newMarker.files[rel] = srcHash;
|
|
52971
53138
|
result.installed.push(rel);
|
|
52972
53139
|
continue;
|
|
@@ -52983,7 +53150,7 @@ function installBundledSkills(opts) {
|
|
|
52983
53150
|
newMarker.packageVersion = marker.packageVersion;
|
|
52984
53151
|
result.skippedOlderPackage.push(rel);
|
|
52985
53152
|
} else if (lastWritten && destHash === lastWritten) {
|
|
52986
|
-
|
|
53153
|
+
fs40.writeFileSync(dest, src);
|
|
52987
53154
|
newMarker.files[rel] = srcHash;
|
|
52988
53155
|
result.updated.push(rel);
|
|
52989
53156
|
} else {
|
|
@@ -52994,20 +53161,27 @@ function installBundledSkills(opts) {
|
|
|
52994
53161
|
}
|
|
52995
53162
|
}
|
|
52996
53163
|
try {
|
|
52997
|
-
|
|
53164
|
+
fs40.writeFileSync(markerPath, JSON.stringify(newMarker, null, 2));
|
|
52998
53165
|
} catch (e) {
|
|
52999
53166
|
result.errors.push(`marker write failed: ${String(e)}`);
|
|
53000
53167
|
}
|
|
53001
53168
|
return result;
|
|
53002
53169
|
}
|
|
53170
|
+
function skillNames(files) {
|
|
53171
|
+
return [...new Set(files.map((rel) => rel.split(/[\\/]/)[0]))].sort();
|
|
53172
|
+
}
|
|
53003
53173
|
function summarizeInstall(r) {
|
|
53004
53174
|
if (!r.bundledDir) return "";
|
|
53005
53175
|
const bits = [];
|
|
53006
|
-
|
|
53007
|
-
|
|
53008
|
-
|
|
53009
|
-
|
|
53010
|
-
|
|
53176
|
+
const bucket = (files, label) => {
|
|
53177
|
+
const names = skillNames(files);
|
|
53178
|
+
if (names.length) bits.push(`${names.length} ${label} (${names.join(", ")})`);
|
|
53179
|
+
};
|
|
53180
|
+
bucket(r.installed, "installed");
|
|
53181
|
+
bucket(r.updated, "updated");
|
|
53182
|
+
bucket(r.skippedUserModified, "kept with your edits");
|
|
53183
|
+
bucket(r.skippedOlderPackage, "kept, newer than this version");
|
|
53184
|
+
if (r.errors.length) bits.push(`${r.errors.length} file error${r.errors.length === 1 ? "" : "s"}`);
|
|
53011
53185
|
if (bits.length === 0) return "";
|
|
53012
53186
|
return `Skills: ${bits.join(", ")} \u2192 ${r.targetDir}`;
|
|
53013
53187
|
}
|
|
@@ -53033,6 +53207,8 @@ function registerSkillsTool(server2, packageVersion, baseDir) {
|
|
|
53033
53207
|
return jsonResponse({
|
|
53034
53208
|
ok: result.errors.length === 0,
|
|
53035
53209
|
targetDir: result.targetDir,
|
|
53210
|
+
// Skills, by name, before the per-file detail (BUG-2026-09-14-03).
|
|
53211
|
+
skills: { installed: skillNames(result.installed), updated: skillNames(result.updated), keptYourEdits: skillNames(result.skippedUserModified) },
|
|
53036
53212
|
installed: result.installed,
|
|
53037
53213
|
updated: result.updated,
|
|
53038
53214
|
keptYourEdits: result.skippedUserModified,
|
|
@@ -53080,11 +53256,11 @@ function agencyPrimer(p) {
|
|
|
53080
53256
|
if (staff.length) lines.push(`Their team: ${staff.join("; ")}.`);
|
|
53081
53257
|
const sops = (p.delivery?.sops ?? []).filter(Boolean);
|
|
53082
53258
|
if (sops.length) lines.push(`How they deliver, in their words: ${sops.map((s) => `"${s}"`).join(" ")}`);
|
|
53083
|
-
const
|
|
53084
|
-
if (
|
|
53085
|
-
if (
|
|
53086
|
-
if (
|
|
53087
|
-
if (
|
|
53259
|
+
const os8 = p.agencyOs;
|
|
53260
|
+
if (os8?.method) lines.push(`Their method: ${os8.method}`);
|
|
53261
|
+
if (os8?.voice) lines.push(`How they sound, and how you should sound for them: ${os8.voice}`);
|
|
53262
|
+
if (os8?.ica?.who) lines.push(`Who they serve: ${os8.ica.who}`);
|
|
53263
|
+
if (os8?.offer?.name || os8?.offer?.promise) lines.push(`Their core offer: ${[os8.offer?.name, os8.offer?.promise].filter(Boolean).join(" \u2014 ")}`);
|
|
53088
53264
|
if (!lines.length) return void 0;
|
|
53089
53265
|
lines.push("These are the agency's own details, not any client's. Client-by-client records are a tool call away, not in here.");
|
|
53090
53266
|
const text = lines.join("\n");
|
|
@@ -53141,7 +53317,7 @@ dotenv2.config();
|
|
|
53141
53317
|
setPkgVersion(pkg.version);
|
|
53142
53318
|
{
|
|
53143
53319
|
const configDirOverride = process.env.GHL_MCP_CONFIG_DIR?.trim();
|
|
53144
|
-
if (configDirOverride && !
|
|
53320
|
+
if (configDirOverride && !path39.isAbsolute(configDirOverride)) {
|
|
53145
53321
|
process.stderr.write(
|
|
53146
53322
|
`[ghl-mcp] GHL_MCP_CONFIG_DIR must be an absolute path (got "${configDirOverride}"). Use e.g. /data/ghl-mcp in a container, with a volume mounted at /data.
|
|
53147
53323
|
`
|
|
@@ -53154,20 +53330,20 @@ process.on("unhandledRejection", (reason2) => {
|
|
|
53154
53330
|
`);
|
|
53155
53331
|
});
|
|
53156
53332
|
function hardenSecretFilePerms() {
|
|
53157
|
-
const repoDir =
|
|
53333
|
+
const repoDir = path39.resolve(__dirname, "..");
|
|
53158
53334
|
const candidates = [
|
|
53159
|
-
{ file:
|
|
53335
|
+
{ file: path39.join(repoDir, "start-mcp.sh"), mode: 448 },
|
|
53160
53336
|
// Legacy registry location (pre-migration); new location lives in app-data.
|
|
53161
|
-
{ file:
|
|
53337
|
+
{ file: path39.join(repoDir, ".ghl-tokens.json"), mode: 384 },
|
|
53162
53338
|
{ file: tokenRegistryPath(), mode: 384 }
|
|
53163
53339
|
];
|
|
53164
53340
|
for (const { file: file2, mode } of candidates) {
|
|
53165
53341
|
let current;
|
|
53166
53342
|
try {
|
|
53167
|
-
if (!
|
|
53168
|
-
current =
|
|
53343
|
+
if (!fs41.existsSync(file2)) continue;
|
|
53344
|
+
current = fs41.statSync(file2).mode & 511;
|
|
53169
53345
|
if (current !== mode) {
|
|
53170
|
-
|
|
53346
|
+
fs41.chmodSync(file2, mode);
|
|
53171
53347
|
}
|
|
53172
53348
|
} catch (error) {
|
|
53173
53349
|
const message = error instanceof Error ? error.message : String(error);
|