@elitedcs/ghl-mcp 3.84.5 → 3.84.7
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 +10 -0
- package/README.md +1 -1
- package/dist/index.js +768 -705
- package/guide/guide.html +8 -9
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -446,18 +446,18 @@ function setProtectedAccount(locationId2, name, file2 = accountRulesPath()) {
|
|
|
446
446
|
function isWriteMethod(method) {
|
|
447
447
|
return WRITE_METHODS.has(method.toUpperCase());
|
|
448
448
|
}
|
|
449
|
-
function locationIdsInRequest(
|
|
449
|
+
function locationIdsInRequest(path41, opts = {}) {
|
|
450
450
|
const found = /* @__PURE__ */ new Set();
|
|
451
|
-
const
|
|
451
|
+
const add2 = (v) => {
|
|
452
452
|
if (typeof v === "string" && v.trim()) found.add(v.trim());
|
|
453
|
-
else if (Array.isArray(v)) v.forEach(
|
|
453
|
+
else if (Array.isArray(v)) v.forEach(add2);
|
|
454
454
|
};
|
|
455
|
-
const seg = /\/locations\/([^/?#]+)/i.exec(
|
|
456
|
-
if (seg)
|
|
455
|
+
const seg = /\/locations\/([^/?#]+)/i.exec(path41);
|
|
456
|
+
if (seg) add2(decodeURIComponent(seg[1]));
|
|
457
457
|
for (const src of [opts.params, opts.body]) {
|
|
458
458
|
if (!src || typeof src !== "object") continue;
|
|
459
459
|
for (const key of ["locationId", "location_id", "locationIds", "location_ids", "targetLocationId"]) {
|
|
460
|
-
|
|
460
|
+
add2(src[key]);
|
|
461
461
|
}
|
|
462
462
|
}
|
|
463
463
|
return [...found];
|
|
@@ -479,7 +479,7 @@ function protectedRefusalFor(locationId2, rulesFile) {
|
|
|
479
479
|
const name = rules.protected[id];
|
|
480
480
|
return name ? protectedRefusalMessage(name, false) : null;
|
|
481
481
|
}
|
|
482
|
-
function assertWriteAllowed(method,
|
|
482
|
+
function assertWriteAllowed(method, path41, opts = {}) {
|
|
483
483
|
if (!isWriteMethod(method)) return;
|
|
484
484
|
let rules;
|
|
485
485
|
try {
|
|
@@ -488,7 +488,7 @@ function assertWriteAllowed(method, path42, opts = {}) {
|
|
|
488
488
|
throw new Error(UNREADABLE_RULES_MSG);
|
|
489
489
|
}
|
|
490
490
|
if (Object.keys(rules.protected).length === 0) return;
|
|
491
|
-
for (const id of locationIdsInRequest(
|
|
491
|
+
for (const id of locationIdsInRequest(path41, opts)) {
|
|
492
492
|
const name = rules.protected[id];
|
|
493
493
|
if (name) throw new Error(protectedRefusalMessage(name, false));
|
|
494
494
|
}
|
|
@@ -646,8 +646,8 @@ var init_ghl_client = __esm({
|
|
|
646
646
|
Version: version || GHL_API_VERSION
|
|
647
647
|
};
|
|
648
648
|
}
|
|
649
|
-
buildUrl(
|
|
650
|
-
const url = new URL(
|
|
649
|
+
buildUrl(path41, params) {
|
|
650
|
+
const url = new URL(path41, GHL_BASE_URL);
|
|
651
651
|
if (params) {
|
|
652
652
|
for (const [key, value] of Object.entries(params)) {
|
|
653
653
|
if (value !== void 0 && value !== null) {
|
|
@@ -657,19 +657,19 @@ var init_ghl_client = __esm({
|
|
|
657
657
|
}
|
|
658
658
|
return url.toString();
|
|
659
659
|
}
|
|
660
|
-
async request(method,
|
|
660
|
+
async request(method, path41, options = {}, attempt = 0) {
|
|
661
661
|
this.signal?.throwIfAborted();
|
|
662
662
|
if (inBuildRequestScope() && method === "GET" && !inAccountReadAttempt()) {
|
|
663
|
-
return retryAccountRead(() => this.request(method,
|
|
663
|
+
return retryAccountRead(() => this.request(method, path41, options, attempt), `${method} ${path41}`);
|
|
664
664
|
}
|
|
665
665
|
if (attempt === 0) {
|
|
666
|
-
assertWriteAllowed(method,
|
|
666
|
+
assertWriteAllowed(method, path41, {
|
|
667
667
|
params: options.params,
|
|
668
668
|
body: options.body,
|
|
669
669
|
defaultLocationId: this.defaultLocationId
|
|
670
670
|
});
|
|
671
671
|
}
|
|
672
|
-
const url = this.buildUrl(
|
|
672
|
+
const url = this.buildUrl(path41, options.params);
|
|
673
673
|
const headers = this.buildHeaders(options.version);
|
|
674
674
|
const fetchOptions = {
|
|
675
675
|
method,
|
|
@@ -690,14 +690,14 @@ var init_ghl_client = __esm({
|
|
|
690
690
|
clearTimeout(timeout);
|
|
691
691
|
this.signal?.throwIfAborted();
|
|
692
692
|
if (error instanceof Error && error.name === "AbortError") {
|
|
693
|
-
throw requestError(new Error(`Request timeout (30s): ${method} ${
|
|
693
|
+
throw requestError(new Error(`Request timeout (30s): ${method} ${path41}`));
|
|
694
694
|
}
|
|
695
695
|
if (!inBuildRequestScope() && !options.noRetry && attempt < MAX_RETRIES) {
|
|
696
696
|
const delay5 = computeRetryDelay(null, attempt, BASE_DELAY_MS);
|
|
697
|
-
process.stderr.write(`[ghl-mcp] Network error on ${method} ${
|
|
697
|
+
process.stderr.write(`[ghl-mcp] Network error on ${method} ${path41}, retry ${attempt + 1}/${MAX_RETRIES} in ${delay5}ms
|
|
698
698
|
`);
|
|
699
699
|
await new Promise((r) => setTimeout(r, delay5));
|
|
700
|
-
return this.request(method,
|
|
700
|
+
return this.request(method, path41, options, attempt + 1);
|
|
701
701
|
}
|
|
702
702
|
throw requestError(error);
|
|
703
703
|
} finally {
|
|
@@ -705,10 +705,10 @@ var init_ghl_client = __esm({
|
|
|
705
705
|
}
|
|
706
706
|
if (!inBuildRequestScope() && !options.noRetry && (response.status === 429 || response.status >= 500) && attempt < MAX_RETRIES) {
|
|
707
707
|
const delay5 = computeRetryDelay(response.headers.get("Retry-After"), attempt, BASE_DELAY_MS);
|
|
708
|
-
process.stderr.write(`[ghl-mcp] ${response.status} on ${method} ${
|
|
708
|
+
process.stderr.write(`[ghl-mcp] ${response.status} on ${method} ${path41}, retry ${attempt + 1}/${MAX_RETRIES} in ${delay5}ms
|
|
709
709
|
`);
|
|
710
710
|
await new Promise((r) => setTimeout(r, delay5));
|
|
711
|
-
return this.request(method,
|
|
711
|
+
return this.request(method, path41, options, attempt + 1);
|
|
712
712
|
}
|
|
713
713
|
if (!response.ok) {
|
|
714
714
|
let errorBody = "";
|
|
@@ -717,7 +717,7 @@ var init_ghl_client = __esm({
|
|
|
717
717
|
} catch {
|
|
718
718
|
}
|
|
719
719
|
throw requestError(new Error(
|
|
720
|
-
`GHL API Error ${response.status} ${response.statusText}: ${method} ${
|
|
720
|
+
`GHL API Error ${response.status} ${response.statusText}: ${method} ${path41}
|
|
721
721
|
${errorBody}`
|
|
722
722
|
));
|
|
723
723
|
}
|
|
@@ -738,20 +738,20 @@ ${errorBody}`
|
|
|
738
738
|
combined?.dispose();
|
|
739
739
|
}
|
|
740
740
|
}
|
|
741
|
-
async get(
|
|
742
|
-
return this.request("GET",
|
|
741
|
+
async get(path41, options) {
|
|
742
|
+
return this.request("GET", path41, options);
|
|
743
743
|
}
|
|
744
|
-
async post(
|
|
745
|
-
return this.request("POST",
|
|
744
|
+
async post(path41, options) {
|
|
745
|
+
return this.request("POST", path41, options);
|
|
746
746
|
}
|
|
747
|
-
async put(
|
|
748
|
-
return this.request("PUT",
|
|
747
|
+
async put(path41, options) {
|
|
748
|
+
return this.request("PUT", path41, options);
|
|
749
749
|
}
|
|
750
|
-
async patch(
|
|
751
|
-
return this.request("PATCH",
|
|
750
|
+
async patch(path41, options) {
|
|
751
|
+
return this.request("PATCH", path41, options);
|
|
752
752
|
}
|
|
753
|
-
async delete(
|
|
754
|
-
return this.request("DELETE",
|
|
753
|
+
async delete(path41, options) {
|
|
754
|
+
return this.request("DELETE", path41, options);
|
|
755
755
|
}
|
|
756
756
|
/**
|
|
757
757
|
* Helper: resolves locationId from args or falls back to default
|
|
@@ -3436,8 +3436,6 @@ var init_tool_filter = __esm({
|
|
|
3436
3436
|
"attach_assessment",
|
|
3437
3437
|
// source GETs plus local card and intake drafts only
|
|
3438
3438
|
"set_income_source",
|
|
3439
|
-
"record_brief_answers",
|
|
3440
|
-
// local chat rows only; no account/provider writes
|
|
3441
3439
|
"connect_system",
|
|
3442
3440
|
"disconnect_system"
|
|
3443
3441
|
]);
|
|
@@ -7107,11 +7105,11 @@ function registerEmailTools(server2, client) {
|
|
|
7107
7105
|
function registerEmailBuilderInternalTools(server2, builderClient, publicClient) {
|
|
7108
7106
|
if (!builderClient) return;
|
|
7109
7107
|
const client = builderClient;
|
|
7110
|
-
async function builderRequest2(method,
|
|
7111
|
-
const response = await builderRequest(client, method, `${EMAIL_BUILDER_BASE}${
|
|
7108
|
+
async function builderRequest2(method, path41, body) {
|
|
7109
|
+
const response = await builderRequest(client, method, `${EMAIL_BUILDER_BASE}${path41}`, body ? { body } : {});
|
|
7112
7110
|
if (!response.ok) {
|
|
7113
7111
|
const text3 = await response.text();
|
|
7114
|
-
throw new Error(`Email Builder API Error ${response.status}: ${method} /emails/builder${
|
|
7112
|
+
throw new Error(`Email Builder API Error ${response.status}: ${method} /emails/builder${path41}
|
|
7115
7113
|
${text3}`);
|
|
7116
7114
|
}
|
|
7117
7115
|
const text2 = await response.text();
|
|
@@ -10921,11 +10919,11 @@ async function readPageFull(client, pageId, fetcher = fetch) {
|
|
|
10921
10919
|
const headers = await client.buildHeaders();
|
|
10922
10920
|
headers.Origin = "https://app.gohighlevel.com";
|
|
10923
10921
|
headers.Referer = "https://app.gohighlevel.com/";
|
|
10924
|
-
const
|
|
10925
|
-
const response = await fetcher(`https://backend.leadconnectorhq.com/funnels${
|
|
10922
|
+
const path41 = `/page/${pageId}?locationId=${client.locationId}`;
|
|
10923
|
+
const response = await fetcher(`https://backend.leadconnectorhq.com/funnels${path41}`, { method: "GET", headers });
|
|
10926
10924
|
if (!response.ok) {
|
|
10927
10925
|
const text3 = await response.text();
|
|
10928
|
-
throw new Error(`Funnel API Error ${response.status}: GET ${
|
|
10926
|
+
throw new Error(`Funnel API Error ${response.status}: GET ${path41}
|
|
10929
10927
|
${text3}`);
|
|
10930
10928
|
}
|
|
10931
10929
|
const text2 = await response.text();
|
|
@@ -10984,20 +10982,20 @@ function registerFunnelBuilderTools(server2, builderClient) {
|
|
|
10984
10982
|
return;
|
|
10985
10983
|
}
|
|
10986
10984
|
const client = builderClient;
|
|
10987
|
-
async function internalGet(
|
|
10988
|
-
return client.request("GET",
|
|
10985
|
+
async function internalGet(path41) {
|
|
10986
|
+
return client.request("GET", path41);
|
|
10989
10987
|
}
|
|
10990
|
-
async function internalPost(
|
|
10991
|
-
return client.request("POST",
|
|
10988
|
+
async function internalPost(path41, body) {
|
|
10989
|
+
return client.request("POST", path41, body);
|
|
10992
10990
|
}
|
|
10993
|
-
async function internalPut(
|
|
10994
|
-
return client.request("PUT",
|
|
10991
|
+
async function internalPut(path41, body) {
|
|
10992
|
+
return client.request("PUT", path41, body);
|
|
10995
10993
|
}
|
|
10996
|
-
async function internalDelete(
|
|
10997
|
-
return client.request("DELETE",
|
|
10994
|
+
async function internalDelete(path41) {
|
|
10995
|
+
return client.request("DELETE", path41);
|
|
10998
10996
|
}
|
|
10999
|
-
async function funnelRequest(method,
|
|
11000
|
-
const url = `https://backend.leadconnectorhq.com/funnels${
|
|
10997
|
+
async function funnelRequest(method, path41, body) {
|
|
10998
|
+
const url = `https://backend.leadconnectorhq.com/funnels${path41}`;
|
|
11001
10999
|
const sendsBody = body !== void 0 && (method === "POST" || method === "PUT");
|
|
11002
11000
|
const response = await builderRequest(client, method, url, {
|
|
11003
11001
|
...sendsBody ? { body } : {},
|
|
@@ -11010,7 +11008,7 @@ function registerFunnelBuilderTools(server2, builderClient) {
|
|
|
11010
11008
|
});
|
|
11011
11009
|
if (!response.ok) {
|
|
11012
11010
|
const text3 = await response.text();
|
|
11013
|
-
throw new Error(`Funnel API Error ${response.status}: ${method} ${
|
|
11011
|
+
throw new Error(`Funnel API Error ${response.status}: ${method} ${path41}
|
|
11014
11012
|
${text3}`);
|
|
11015
11013
|
}
|
|
11016
11014
|
const text2 = await response.text();
|
|
@@ -11601,17 +11599,17 @@ function requireCompany(api) {
|
|
|
11601
11599
|
}
|
|
11602
11600
|
return company;
|
|
11603
11601
|
}
|
|
11604
|
-
async function call(api,
|
|
11602
|
+
async function call(api, path41, init) {
|
|
11605
11603
|
const headers = { ...await api.headers(), Origin: "https://app.gohighlevel.com", Referer: "https://app.gohighlevel.com/" };
|
|
11606
|
-
if (MUTATING_PATHS.some((mutating) =>
|
|
11607
|
-
assertWriteAllowed("POST", SVC +
|
|
11604
|
+
if (MUTATING_PATHS.some((mutating) => path41.startsWith(mutating))) {
|
|
11605
|
+
assertWriteAllowed("POST", SVC + path41, { defaultLocationId: api.locationId() });
|
|
11608
11606
|
}
|
|
11609
11607
|
const doFetch = api.fetch ?? fetch;
|
|
11610
|
-
const response = await doFetch(SVC +
|
|
11608
|
+
const response = await doFetch(SVC + path41, { ...init, headers });
|
|
11611
11609
|
const text2 = await response.text();
|
|
11612
11610
|
if (!response.ok) {
|
|
11613
11611
|
const hint = response.status === 401 && /not authorized to access this company/i.test(text2) ? " \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." : "";
|
|
11614
|
-
throw new Error(`GoHighLevel template library ${response.status} on ${
|
|
11612
|
+
throw new Error(`GoHighLevel template library ${response.status} on ${path41}${hint}
|
|
11615
11613
|
${text2.slice(0, 300)}`);
|
|
11616
11614
|
}
|
|
11617
11615
|
return text2 ? JSON.parse(text2) : {};
|
|
@@ -11968,12 +11966,12 @@ function normaliseColour(value) {
|
|
|
11968
11966
|
function renderedPalette(envelope) {
|
|
11969
11967
|
const defs = pageVariables(envelope);
|
|
11970
11968
|
const paints = [];
|
|
11971
|
-
const walk = (node,
|
|
11972
|
-
if (Array.isArray(node)) return void node.forEach((x, i) => walk(x, `${
|
|
11969
|
+
const walk = (node, path41, prop) => {
|
|
11970
|
+
if (Array.isArray(node)) return void node.forEach((x, i) => walk(x, `${path41}[${i}]`, prop));
|
|
11973
11971
|
if (isRecord8(node)) {
|
|
11974
11972
|
for (const [k, x] of Object.entries(node)) {
|
|
11975
11973
|
if (DEFINITION_KEYS.has(k) || k.startsWith("--")) continue;
|
|
11976
|
-
walk(x, `${
|
|
11974
|
+
walk(x, `${path41}.${k}`, WRAPPER_KEYS.has(k) ? prop : k);
|
|
11977
11975
|
}
|
|
11978
11976
|
return;
|
|
11979
11977
|
}
|
|
@@ -11983,7 +11981,7 @@ function renderedPalette(envelope) {
|
|
|
11983
11981
|
const resolved2 = resolveValue(raw, defs);
|
|
11984
11982
|
const colour = normaliseColour(resolved2);
|
|
11985
11983
|
if (colour === null && resolved2 !== null) return;
|
|
11986
|
-
paints.push({ path:
|
|
11984
|
+
paints.push({ path: path41, prop, kind: kindOf(prop), colour });
|
|
11987
11985
|
};
|
|
11988
11986
|
walk(envelope, "", "");
|
|
11989
11987
|
const face = (name, settingsKey) => {
|
|
@@ -12078,7 +12076,7 @@ var init_rendered_palette = __esm({
|
|
|
12078
12076
|
VISIBLE_CHANGE = 0.05;
|
|
12079
12077
|
BARELY_SHARE = 0.5;
|
|
12080
12078
|
CARRIES_COLOUR = 0.02;
|
|
12081
|
-
paintGroup = (
|
|
12079
|
+
paintGroup = (path41) => /^\.(sections|popups)\[\d+\]/.exec(path41)?.[0]?.slice(1) ?? "page";
|
|
12082
12080
|
looksDifferent = (a, b) => {
|
|
12083
12081
|
if (a === b) return false;
|
|
12084
12082
|
if (a === "transparent" || b === "transparent" || a.startsWith("rgba") || b.startsWith("rgba")) return a !== b;
|
|
@@ -16653,9 +16651,9 @@ function removalHoldBack(opts) {
|
|
|
16653
16651
|
if (opts.multiPageStepNames.length > 0) return `${opts.multiPageStepNames.length} step(s) carry more than one page (${opts.multiPageStepNames.join(", ")}) and only the first of each was rebuilt`;
|
|
16654
16652
|
return null;
|
|
16655
16653
|
}
|
|
16656
|
-
function brandedPath(
|
|
16654
|
+
function brandedPath(path41, name, taken) {
|
|
16657
16655
|
const normalize = (s) => s.replace(/^\/+|\/+$/g, "").toLowerCase();
|
|
16658
|
-
const base = normalize(
|
|
16656
|
+
const base = normalize(path41 ?? "") || name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "") || "page";
|
|
16659
16657
|
const used = new Set([...taken].map(normalize));
|
|
16660
16658
|
let candidate = `${base}-branded`;
|
|
16661
16659
|
for (let n = 2; used.has(candidate); n++) candidate = `${base}-branded-${n}`;
|
|
@@ -16683,9 +16681,9 @@ function rewriteTemplateLinks(envelope, destinations) {
|
|
|
16683
16681
|
representation = "page";
|
|
16684
16682
|
matches = destinations.filter((d2) => d2.pageId === preview[2]);
|
|
16685
16683
|
} else if (!/^(?:[a-z][a-z0-9+.-]*:|\/\/)/i.test(from) && !from.startsWith("#")) {
|
|
16686
|
-
const [
|
|
16687
|
-
suffix = from.slice(
|
|
16688
|
-
matches = destinations.filter((d2) => d2.path && normalize(d2.path) === normalize(
|
|
16684
|
+
const [path41] = from.split(/[?#]/);
|
|
16685
|
+
suffix = from.slice(path41.length);
|
|
16686
|
+
matches = destinations.filter((d2) => d2.path && normalize(d2.path) === normalize(path41));
|
|
16689
16687
|
}
|
|
16690
16688
|
}
|
|
16691
16689
|
if (matches.length !== 1) {
|
|
@@ -16768,8 +16766,8 @@ function registerPageStudioTools(server2, builderClient) {
|
|
|
16768
16766
|
}
|
|
16769
16767
|
return { map: out, truncated };
|
|
16770
16768
|
}
|
|
16771
|
-
async function funnelRequest(method,
|
|
16772
|
-
const response = await builderRequest(client, method, `https://backend.leadconnectorhq.com/funnels${
|
|
16769
|
+
async function funnelRequest(method, path41, body) {
|
|
16770
|
+
const response = await builderRequest(client, method, `https://backend.leadconnectorhq.com/funnels${path41}`, {
|
|
16773
16771
|
...body ? { body } : {},
|
|
16774
16772
|
headers: {
|
|
16775
16773
|
// Internal-API calls require the app origin (autosave needs the
|
|
@@ -16778,7 +16776,7 @@ function registerPageStudioTools(server2, builderClient) {
|
|
|
16778
16776
|
Referer: "https://app.gohighlevel.com/"
|
|
16779
16777
|
}
|
|
16780
16778
|
});
|
|
16781
|
-
if (!response.ok) throw new Error(`Funnel API Error ${response.status}: ${method} ${
|
|
16779
|
+
if (!response.ok) throw new Error(`Funnel API Error ${response.status}: ${method} ${path41}
|
|
16782
16780
|
${await response.text()}`);
|
|
16783
16781
|
const text2 = await response.text();
|
|
16784
16782
|
return text2 ? JSON.parse(text2) : {};
|
|
@@ -17647,13 +17645,13 @@ function cleanFormDataForSave(formData) {
|
|
|
17647
17645
|
const cleaned = dropRedundantCaseCollidingKeys(formData);
|
|
17648
17646
|
return isJsonObject(cleaned) ? cleaned : formData;
|
|
17649
17647
|
}
|
|
17650
|
-
async function formApiRequest(client, method,
|
|
17651
|
-
const url = `https://backend.leadconnectorhq.com/forms${
|
|
17648
|
+
async function formApiRequest(client, method, path41, body) {
|
|
17649
|
+
const url = `https://backend.leadconnectorhq.com/forms${path41}`;
|
|
17652
17650
|
const sendsBody = body !== void 0 && (method === "POST" || method === "PUT");
|
|
17653
17651
|
const response = await builderRequest(client, method, url, sendsBody ? { body } : {});
|
|
17654
17652
|
if (!response.ok) {
|
|
17655
17653
|
const text3 = await response.text();
|
|
17656
|
-
throw new Error(`Form API Error ${response.status}: ${method} ${
|
|
17654
|
+
throw new Error(`Form API Error ${response.status}: ${method} ${path41}
|
|
17657
17655
|
${text3}`);
|
|
17658
17656
|
}
|
|
17659
17657
|
const text2 = await response.text();
|
|
@@ -17695,7 +17693,7 @@ function registerFormBuilderTools(server2, builderClient, publicClient) {
|
|
|
17695
17693
|
);
|
|
17696
17694
|
return;
|
|
17697
17695
|
}
|
|
17698
|
-
const formRequest = (method,
|
|
17696
|
+
const formRequest = (method, path41, body) => formApiRequest(client, method, path41, body);
|
|
17699
17697
|
server2.tool(
|
|
17700
17698
|
"get_form_full",
|
|
17701
17699
|
"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.",
|
|
@@ -17829,10 +17827,10 @@ function registerFormBuilderTools(server2, builderClient, publicClient) {
|
|
|
17829
17827
|
},
|
|
17830
17828
|
async ({ formId, limit, skip }) => {
|
|
17831
17829
|
try {
|
|
17832
|
-
let
|
|
17833
|
-
if (formId)
|
|
17834
|
-
if (skip)
|
|
17835
|
-
const result = await formRequest("GET",
|
|
17830
|
+
let path41 = `/submissions?locationId=${client.locationId}&limit=${limit ?? 20}`;
|
|
17831
|
+
if (formId) path41 += `&formId=${formId}`;
|
|
17832
|
+
if (skip) path41 += `&skip=${skip}`;
|
|
17833
|
+
const result = await formRequest("GET", path41);
|
|
17836
17834
|
return {
|
|
17837
17835
|
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
17838
17836
|
};
|
|
@@ -18240,13 +18238,13 @@ var init_funnel_qa = __esm({
|
|
|
18240
18238
|
function registerPipelineBuilderTools(server2, builderClient) {
|
|
18241
18239
|
if (!builderClient) return;
|
|
18242
18240
|
const client = builderClient;
|
|
18243
|
-
async function pipelineRequest(method,
|
|
18244
|
-
const url = `https://backend.leadconnectorhq.com/opportunities/pipelines${
|
|
18241
|
+
async function pipelineRequest(method, path41, body) {
|
|
18242
|
+
const url = `https://backend.leadconnectorhq.com/opportunities/pipelines${path41}`;
|
|
18245
18243
|
const sendsBody = body !== void 0 && (method === "POST" || method === "PUT" || method === "PATCH");
|
|
18246
18244
|
const response = await builderRequest(client, method, url, sendsBody ? { body } : {});
|
|
18247
18245
|
if (!response.ok) {
|
|
18248
18246
|
const text3 = await response.text();
|
|
18249
|
-
throw new Error(`Pipeline API Error ${response.status}: ${method} ${
|
|
18247
|
+
throw new Error(`Pipeline API Error ${response.status}: ${method} ${path41}
|
|
18250
18248
|
${text3}`);
|
|
18251
18249
|
}
|
|
18252
18250
|
const text2 = await response.text();
|
|
@@ -20006,56 +20004,17 @@ var init_brief_deadline = __esm({
|
|
|
20006
20004
|
}
|
|
20007
20005
|
});
|
|
20008
20006
|
|
|
20009
|
-
// src/command-os/connectors/brief-
|
|
20010
|
-
|
|
20011
|
-
|
|
20012
|
-
|
|
20013
|
-
} catch (error) {
|
|
20014
|
-
if (error instanceof Error && "code" in error && error.code === "ENOENT") return {};
|
|
20015
|
-
throw new Error("The saved chat brief could not be read; nothing was replaced.");
|
|
20016
|
-
}
|
|
20017
|
-
}
|
|
20018
|
-
function recordBriefAnswers(input) {
|
|
20019
|
-
const { source, readAt, rows: rows2 } = BriefAnswersInput.parse(input);
|
|
20020
|
-
const records = readBriefAnswers();
|
|
20021
|
-
records[source] = { readAt, rows: rows2 };
|
|
20022
|
-
ensureAppDataDir();
|
|
20023
|
-
const tmp = `${briefAnswersPath()}.${(0, import_node_crypto4.randomUUID)()}.tmp`;
|
|
20024
|
-
try {
|
|
20025
|
-
fs11.writeFileSync(tmp, JSON.stringify(records) + "\n", { mode: 384, flag: "wx" });
|
|
20026
|
-
fs11.renameSync(tmp, briefAnswersPath());
|
|
20027
|
-
} catch {
|
|
20028
|
-
throw new Error("The chat brief could not be saved privately on this machine.");
|
|
20029
|
-
} finally {
|
|
20030
|
-
try {
|
|
20031
|
-
fs11.unlinkSync(tmp);
|
|
20032
|
-
} catch {
|
|
20033
|
-
}
|
|
20034
|
-
}
|
|
20035
|
-
return { source, rows: rows2.length, readAt };
|
|
20036
|
-
}
|
|
20037
|
-
var fs11, path10, import_node_crypto4, import_zod49, BriefRowSchema, BriefAnswersInput, RecordSchema, FileSchema, briefAnswersPath;
|
|
20038
|
-
var init_brief_answers = __esm({
|
|
20039
|
-
"src/command-os/connectors/brief-answers.ts"() {
|
|
20007
|
+
// src/command-os/connectors/brief-row.ts
|
|
20008
|
+
var import_zod49, BriefRowSchema;
|
|
20009
|
+
var init_brief_row = __esm({
|
|
20010
|
+
"src/command-os/connectors/brief-row.ts"() {
|
|
20040
20011
|
"use strict";
|
|
20041
|
-
fs11 = __toESM(require("node:fs"));
|
|
20042
|
-
path10 = __toESM(require("node:path"));
|
|
20043
|
-
import_node_crypto4 = require("node:crypto");
|
|
20044
20012
|
import_zod49 = require("zod");
|
|
20045
|
-
init_credentials_store();
|
|
20046
20013
|
BriefRowSchema = import_zod49.z.object({
|
|
20047
20014
|
text: import_zod49.z.string().min(1).max(12e3),
|
|
20048
20015
|
when: import_zod49.z.string().max(300).optional(),
|
|
20049
20016
|
link: import_zod49.z.url().refine((value) => ["https:", "http:"].includes(new URL(value).protocol), "Use an HTTP or HTTPS link.").optional()
|
|
20050
20017
|
}).strict();
|
|
20051
|
-
BriefAnswersInput = import_zod49.z.object({
|
|
20052
|
-
source: import_zod49.z.enum(["gmail", "google-drive"]),
|
|
20053
|
-
readAt: import_zod49.z.iso.datetime({ offset: true }),
|
|
20054
|
-
rows: import_zod49.z.array(BriefRowSchema).max(50, "Record at most 50 rows per source.")
|
|
20055
|
-
}).strict();
|
|
20056
|
-
RecordSchema = BriefAnswersInput.omit({ source: true });
|
|
20057
|
-
FileSchema = import_zod49.z.object({ gmail: RecordSchema.optional(), "google-drive": RecordSchema.optional() }).strict();
|
|
20058
|
-
briefAnswersPath = () => path10.join(appDataDir(), "brief-answers.json");
|
|
20059
20018
|
}
|
|
20060
20019
|
});
|
|
20061
20020
|
|
|
@@ -20079,13 +20038,13 @@ var init_registry = __esm({
|
|
|
20079
20038
|
builtState: "built",
|
|
20080
20039
|
whatItIs: "A restricted key is a password Stripe issues that lets this machine read only the parts of your account you allow.",
|
|
20081
20040
|
checkedOn: null,
|
|
20082
|
-
gives: "Adds
|
|
20083
|
-
needs: ["Access to your Stripe account's API keys.", "A restricted key with Read access to Customers, Invoices and Subscriptions."],
|
|
20041
|
+
gives: "Adds your monthly recurring revenue, money received, failed payments, who owes you and when clients renew to your brief.",
|
|
20042
|
+
needs: ["Access to your Stripe account's API keys.", "A restricted key with Read access to Customers, Invoices and Subscriptions, plus Charges and Products for money received and revenue by product."],
|
|
20084
20043
|
steps: [
|
|
20085
20044
|
"[UNVERIFIED] Sign in to your Stripe Dashboard and choose the account you want to read.",
|
|
20086
20045
|
"[UNVERIFIED] Open Developers, then API keys. If your dashboard shows Workbench, open its API keys page.",
|
|
20087
20046
|
"[UNVERIFIED] Under Restricted keys, choose Create restricted key and give it a name you recognise.",
|
|
20088
|
-
"[UNVERIFIED] Set Customers, Invoices and
|
|
20047
|
+
"[UNVERIFIED] Set Customers, Invoices, Subscriptions, Charges and Products to Read. Leave other permissions at None, then create the key.",
|
|
20089
20048
|
"[UNVERIFIED] Copy the key into the password field on the Stripe card on the Connections screen, add an account label if useful, and choose Connect. Use a live-mode key for real income."
|
|
20090
20049
|
],
|
|
20091
20050
|
say: ["Connect my Stripe.", "Sync my Stripe.", "Run my morning brief."],
|
|
@@ -20129,22 +20088,20 @@ var init_registry = __esm({
|
|
|
20129
20088
|
tools: { connect: "connect_google_calendar", disconnect: "connect_google_calendar", read: "read_my_calendar" }
|
|
20130
20089
|
},
|
|
20131
20090
|
{
|
|
20132
|
-
panel: (deps) => deps.chat("gmail", "Gmail"),
|
|
20133
20091
|
id: "gmail",
|
|
20134
20092
|
name: "Gmail",
|
|
20135
20093
|
builtState: "via-claude",
|
|
20136
20094
|
whatItIs: "Your Claude's Gmail connection lets Claude read the mail you permit in your conversation.",
|
|
20137
20095
|
checkedOn: null,
|
|
20138
20096
|
viaClaude: "Answered by your Claude's Gmail connector; nothing to connect here",
|
|
20139
|
-
gives: "
|
|
20097
|
+
gives: "Your Claude reads client email waiting on you in its own morning brief. The cockpit does not show mail.",
|
|
20140
20098
|
needs: ["Gmail connected in your Claude."],
|
|
20141
20099
|
steps: [],
|
|
20142
|
-
say: ["Run my morning brief
|
|
20100
|
+
say: ["Run my morning brief.", "What client email is waiting on me?"],
|
|
20143
20101
|
revoke: "Disconnect Gmail in your Claude's connector settings.",
|
|
20144
20102
|
tools: {}
|
|
20145
20103
|
},
|
|
20146
20104
|
{
|
|
20147
|
-
panel: (deps) => deps.chat("google-drive", "Google Drive"),
|
|
20148
20105
|
id: "google-drive",
|
|
20149
20106
|
name: "Google Drive",
|
|
20150
20107
|
builtState: "via-claude",
|
|
@@ -20154,11 +20111,10 @@ var init_registry = __esm({
|
|
|
20154
20111
|
gives: "Finds a client's agreement, onboarding form and brand files on request through your Claude's Drive connector.",
|
|
20155
20112
|
needs: ["Google Drive connected in your Claude."],
|
|
20156
20113
|
steps: [],
|
|
20157
|
-
say: ["Find this client's agreement, onboarding form and brand files in Google Drive, using their name and domain."
|
|
20114
|
+
say: ["Find this client's agreement, onboarding form and brand files in Google Drive, using their name and domain."],
|
|
20158
20115
|
revoke: "Disconnect Google Drive in your Claude's connector settings.",
|
|
20159
20116
|
tools: {}
|
|
20160
20117
|
},
|
|
20161
|
-
{ id: "meta-ads", name: "Meta Ads", builtState: "not-built", whatItIs: "", checkedOn: null, gives: "Not built yet", needs: [], steps: [], say: [], revoke: "", tools: {} },
|
|
20162
20118
|
{ id: "zoom", name: "Zoom", builtState: "not-built", whatItIs: "", checkedOn: null, gives: "Not built yet", needs: [], steps: [], say: [], revoke: "", tools: {} },
|
|
20163
20119
|
{ id: "plaud", name: "Plaud", builtState: "not-built", whatItIs: "", checkedOn: null, gives: "Not built yet", needs: [], steps: [], say: [], revoke: "", tools: {} }
|
|
20164
20120
|
];
|
|
@@ -20167,11 +20123,11 @@ var init_registry = __esm({
|
|
|
20167
20123
|
|
|
20168
20124
|
// src/command-os/connectors/store.ts
|
|
20169
20125
|
function connectorsPath() {
|
|
20170
|
-
return
|
|
20126
|
+
return path10.join(appDataDir(), "connectors.json");
|
|
20171
20127
|
}
|
|
20172
20128
|
function readFile() {
|
|
20173
20129
|
try {
|
|
20174
|
-
const raw =
|
|
20130
|
+
const raw = fs11.readFileSync(connectorsPath(), "utf-8");
|
|
20175
20131
|
const parsed2 = JSON.parse(raw);
|
|
20176
20132
|
if (!parsed2 || typeof parsed2 !== "object" || !parsed2.connectors) return { v: 1, connectors: {} };
|
|
20177
20133
|
return parsed2;
|
|
@@ -20183,7 +20139,7 @@ function readForWrite() {
|
|
|
20183
20139
|
const target = connectorsPath();
|
|
20184
20140
|
let raw;
|
|
20185
20141
|
try {
|
|
20186
|
-
raw =
|
|
20142
|
+
raw = fs11.readFileSync(target, "utf-8");
|
|
20187
20143
|
} catch (e) {
|
|
20188
20144
|
if (e?.code === "ENOENT") return { v: 1, connectors: {} };
|
|
20189
20145
|
throw new Error("Your connector file could not be read, so nothing was changed. Nothing was lost.");
|
|
@@ -20195,8 +20151,8 @@ function readForWrite() {
|
|
|
20195
20151
|
} catch {
|
|
20196
20152
|
const kept = `${target}.unreadable-${Date.now()}`;
|
|
20197
20153
|
try {
|
|
20198
|
-
|
|
20199
|
-
|
|
20154
|
+
fs11.copyFileSync(target, kept);
|
|
20155
|
+
fs11.chmodSync(kept, 384);
|
|
20200
20156
|
} catch {
|
|
20201
20157
|
}
|
|
20202
20158
|
throw new Error(
|
|
@@ -20210,31 +20166,36 @@ function writeFile(f) {
|
|
|
20210
20166
|
const tmp = `${target}.${process.pid}.tmp`;
|
|
20211
20167
|
let renamed = false;
|
|
20212
20168
|
try {
|
|
20213
|
-
|
|
20169
|
+
fs11.writeFileSync(tmp, JSON.stringify(f, null, 2) + "\n", { encoding: "utf-8", mode: 384 });
|
|
20214
20170
|
try {
|
|
20215
|
-
|
|
20171
|
+
fs11.chmodSync(tmp, 384);
|
|
20216
20172
|
} catch {
|
|
20217
20173
|
}
|
|
20218
|
-
|
|
20174
|
+
fs11.renameSync(tmp, target);
|
|
20219
20175
|
renamed = true;
|
|
20220
20176
|
} finally {
|
|
20221
20177
|
if (!renamed) {
|
|
20222
20178
|
try {
|
|
20223
|
-
|
|
20179
|
+
fs11.unlinkSync(tmp);
|
|
20224
20180
|
} catch {
|
|
20225
20181
|
}
|
|
20226
20182
|
}
|
|
20227
20183
|
}
|
|
20228
20184
|
try {
|
|
20229
|
-
|
|
20185
|
+
fs11.chmodSync(target, 384);
|
|
20230
20186
|
} catch {
|
|
20231
20187
|
}
|
|
20232
20188
|
}
|
|
20233
20189
|
function safeLabel(label, key) {
|
|
20234
|
-
|
|
20235
|
-
|
|
20236
|
-
const
|
|
20237
|
-
|
|
20190
|
+
if (label === void 0) return label;
|
|
20191
|
+
let out = label;
|
|
20192
|
+
for (const value of [.../* @__PURE__ */ new Set([key, key.trim()])].filter(Boolean).sort((a, b) => b.length - a.length)) {
|
|
20193
|
+
for (let length = value.length; length >= 8; length--) {
|
|
20194
|
+
const part = value.slice(0, length);
|
|
20195
|
+
if (out.includes(part)) out = out.split(part).join("[key removed from label]");
|
|
20196
|
+
}
|
|
20197
|
+
}
|
|
20198
|
+
return out;
|
|
20238
20199
|
}
|
|
20239
20200
|
function connect(system, key, label, now = () => /* @__PURE__ */ new Date(), verified = true) {
|
|
20240
20201
|
const trimmed = String(key ?? "").trim();
|
|
@@ -20257,7 +20218,7 @@ function summaryOf(s) {
|
|
|
20257
20218
|
}
|
|
20258
20219
|
function listConnectors(options = {}) {
|
|
20259
20220
|
try {
|
|
20260
|
-
const file2 = import_zod50.z.object({ v: import_zod50.z.literal(1), connectors: import_zod50.z.record(import_zod50.z.string(), import_zod50.z.unknown()) }).parse(JSON.parse(
|
|
20221
|
+
const file2 = import_zod50.z.object({ v: import_zod50.z.literal(1), connectors: import_zod50.z.record(import_zod50.z.string(), import_zod50.z.unknown()) }).parse(JSON.parse(fs11.readFileSync(connectorsPath(), "utf8")));
|
|
20261
20222
|
const summaries = [];
|
|
20262
20223
|
for (const [system, value] of Object.entries(file2.connectors)) {
|
|
20263
20224
|
if (!SystemSchema.safeParse(system).success) continue;
|
|
@@ -20283,16 +20244,16 @@ async function withKey(system, fn) {
|
|
|
20283
20244
|
if (!s) throw new Error(`${system} is not connected on this machine.`);
|
|
20284
20245
|
return fn(s.key);
|
|
20285
20246
|
}
|
|
20286
|
-
var
|
|
20247
|
+
var fs11, import_zod50, path10, hintOf, SystemSchema, SummarySourceSchema;
|
|
20287
20248
|
var init_store2 = __esm({
|
|
20288
20249
|
"src/command-os/connectors/store.ts"() {
|
|
20289
20250
|
"use strict";
|
|
20290
|
-
|
|
20251
|
+
fs11 = __toESM(require("node:fs"));
|
|
20291
20252
|
import_zod50 = require("zod");
|
|
20292
|
-
|
|
20253
|
+
path10 = __toESM(require("node:path"));
|
|
20293
20254
|
init_credentials_store();
|
|
20294
20255
|
hintOf = (key) => `\u2026${String(key).slice(-4)}`;
|
|
20295
|
-
SystemSchema = import_zod50.z.enum(["ghl", "stripe", "google-workspace", "meeting-capture", "slack", "
|
|
20256
|
+
SystemSchema = import_zod50.z.enum(["ghl", "stripe", "google-workspace", "meeting-capture", "slack", "byo"]);
|
|
20296
20257
|
SummarySourceSchema = import_zod50.z.object({
|
|
20297
20258
|
system: SystemSchema,
|
|
20298
20259
|
key: import_zod50.z.string().min(1),
|
|
@@ -20661,7 +20622,83 @@ async function health(fetcher, now = () => /* @__PURE__ */ new Date()) {
|
|
|
20661
20622
|
return { system: SYSTEM, state: "BROKEN", detail: `Stripe read failed: ${msg3.slice(0, 120)}`, checkedAt };
|
|
20662
20623
|
}
|
|
20663
20624
|
}
|
|
20664
|
-
|
|
20625
|
+
function productGroup(name, all) {
|
|
20626
|
+
const parents = all.filter((p) => p !== name && name.startsWith(p) && /^\s*[\-—–:(+]/.test(name.slice(p.length)));
|
|
20627
|
+
return parents.sort((a, b) => b.length - a.length)[0] ?? name;
|
|
20628
|
+
}
|
|
20629
|
+
function moneySummary(subs, invoices, charges, products, now = /* @__PURE__ */ new Date()) {
|
|
20630
|
+
const names = new Map(products.map((p) => [p.id, p.name]));
|
|
20631
|
+
const allNames = [...new Set(products.map((p) => p.name))];
|
|
20632
|
+
const mrr = /* @__PURE__ */ new Map();
|
|
20633
|
+
const groups = /* @__PURE__ */ new Map();
|
|
20634
|
+
let mrrUnpriced = 0;
|
|
20635
|
+
for (const sub of subs) {
|
|
20636
|
+
if (sub.status !== "active" && sub.status !== "past_due") continue;
|
|
20637
|
+
for (const item of sub.items?.data ?? []) {
|
|
20638
|
+
const price = item.price;
|
|
20639
|
+
const currency = upper(price?.currency) ?? upper(sub.currency);
|
|
20640
|
+
const per = PER_MONTH[price?.recurring?.interval ?? ""];
|
|
20641
|
+
const count2 = price?.recurring?.interval_count ?? 1;
|
|
20642
|
+
const unit = fromMinor(price?.unit_amount, currency);
|
|
20643
|
+
if (!currency || per === void 0 || unit === null || !(count2 > 0)) {
|
|
20644
|
+
mrrUnpriced++;
|
|
20645
|
+
continue;
|
|
20646
|
+
}
|
|
20647
|
+
const monthly = unit * (item.quantity ?? 1) * per / count2;
|
|
20648
|
+
add(mrr, currency, monthly);
|
|
20649
|
+
const product = price?.product ? names.get(price.product) ?? "A product no longer in Stripe" : "Unnamed product";
|
|
20650
|
+
const group = productGroup(product, allNames);
|
|
20651
|
+
const key = `${group}\0${currency}`;
|
|
20652
|
+
const g = groups.get(key) ?? { subs: /* @__PURE__ */ new Set(), currency, monthly: 0 };
|
|
20653
|
+
g.subs.add(sub.id);
|
|
20654
|
+
g.monthly += monthly;
|
|
20655
|
+
groups.set(key, g);
|
|
20656
|
+
}
|
|
20657
|
+
}
|
|
20658
|
+
const nowS = now.getTime() / 1e3;
|
|
20659
|
+
const within = (t, days) => t <= nowS && nowS - t <= days * 86400;
|
|
20660
|
+
const r7 = /* @__PURE__ */ new Map(), r30 = /* @__PURE__ */ new Map();
|
|
20661
|
+
let c7 = 0, c30 = 0, failed30 = 0;
|
|
20662
|
+
for (const c of charges) {
|
|
20663
|
+
if (!within(c.created, 30)) continue;
|
|
20664
|
+
if (c.status === "failed") {
|
|
20665
|
+
failed30++;
|
|
20666
|
+
continue;
|
|
20667
|
+
}
|
|
20668
|
+
if (c.status !== "succeeded") continue;
|
|
20669
|
+
const currency = upper(c.currency);
|
|
20670
|
+
const net = fromMinor(c.amount - (c.amount_refunded ?? 0), currency) ?? 0;
|
|
20671
|
+
if (net <= 0) continue;
|
|
20672
|
+
add(r30, currency, net);
|
|
20673
|
+
c30++;
|
|
20674
|
+
if (within(c.created, 7)) {
|
|
20675
|
+
add(r7, currency, net);
|
|
20676
|
+
c7++;
|
|
20677
|
+
}
|
|
20678
|
+
}
|
|
20679
|
+
const recurring = /* @__PURE__ */ new Map();
|
|
20680
|
+
for (const i of invoices) {
|
|
20681
|
+
const paidAt = i.status_transitions?.paid_at;
|
|
20682
|
+
const subId = i.parent?.subscription_details?.subscription ?? i.subscription;
|
|
20683
|
+
if (i.status !== "paid" || !paidAt || !within(paidAt, 30) || !subId) continue;
|
|
20684
|
+
const currency = upper(i.currency);
|
|
20685
|
+
const paid = fromMinor(i.amount_paid, currency);
|
|
20686
|
+
if (currency && paid) add(recurring, currency, paid);
|
|
20687
|
+
}
|
|
20688
|
+
const oneOff = /* @__PURE__ */ new Map();
|
|
20689
|
+
for (const [currency, amount] of r30) oneOff.set(currency, Math.max(0, amount - (recurring.get(currency) ?? 0)));
|
|
20690
|
+
return {
|
|
20691
|
+
mrr: totals(mrr),
|
|
20692
|
+
mrrUnpriced,
|
|
20693
|
+
byProduct: [...groups].map(([key, g]) => ({ name: key.split("\0")[0], subscribers: g.subs.size, currency: g.currency, monthly: Math.round(g.monthly * 100) / 100 })).sort((a, b) => b.monthly - a.monthly),
|
|
20694
|
+
received7: { count: c7, totals: totals(r7) },
|
|
20695
|
+
received30: { count: c30, totals: totals(r30) },
|
|
20696
|
+
recurring30: totals(recurring),
|
|
20697
|
+
oneOff30: totals(oneOff),
|
|
20698
|
+
failed30
|
|
20699
|
+
};
|
|
20700
|
+
}
|
|
20701
|
+
var SYSTEM, iso, ZERO_DECIMAL, THREE_DECIMAL, fromMinor, upper, stillOwed, PER_MONTH, add, totals;
|
|
20665
20702
|
var init_stripe = __esm({
|
|
20666
20703
|
"src/command-os/connectors/stripe.ts"() {
|
|
20667
20704
|
"use strict";
|
|
@@ -20681,13 +20718,16 @@ var init_stripe = __esm({
|
|
|
20681
20718
|
return t ? t : null;
|
|
20682
20719
|
};
|
|
20683
20720
|
stillOwed = (i) => (fromMinor(i.amount_remaining ?? i.amount_due ?? null, upper(i.currency)) ?? 0) > 0;
|
|
20721
|
+
PER_MONTH = { day: 365 / 12, week: 52 / 12, month: 1, year: 1 / 12 };
|
|
20722
|
+
add = (m, currency, amount) => m.set(currency, (m.get(currency) ?? 0) + amount);
|
|
20723
|
+
totals = (m) => [...m].map(([currency, amount]) => ({ currency, amount: Math.round(amount * 100) / 100 }));
|
|
20684
20724
|
}
|
|
20685
20725
|
});
|
|
20686
20726
|
|
|
20687
20727
|
// src/command-os/connectors/google-calendar-store.ts
|
|
20688
20728
|
function read() {
|
|
20689
20729
|
try {
|
|
20690
|
-
return SecretSchema.parse(JSON.parse(
|
|
20730
|
+
return SecretSchema.parse(JSON.parse(fs12.readFileSync(googleCalendarPath(), "utf8")));
|
|
20691
20731
|
} catch (error) {
|
|
20692
20732
|
if (error.code === "ENOENT") return null;
|
|
20693
20733
|
throw new Error("The Google Calendar sign-in on this machine could not be read. Connect your Google Calendar again.");
|
|
@@ -20710,16 +20750,16 @@ function saveCalendarSecret(secret) {
|
|
|
20710
20750
|
try {
|
|
20711
20751
|
const validated = SecretSchema.parse(secret);
|
|
20712
20752
|
const dir = ensureAppDataDir();
|
|
20713
|
-
if (process.platform !== "win32")
|
|
20714
|
-
tmp = `${googleCalendarPath()}.${(0,
|
|
20715
|
-
|
|
20716
|
-
|
|
20753
|
+
if (process.platform !== "win32") fs12.chmodSync(dir, 448);
|
|
20754
|
+
tmp = `${googleCalendarPath()}.${(0, import_node_crypto4.randomUUID)()}.tmp`;
|
|
20755
|
+
fs12.writeFileSync(tmp, JSON.stringify(validated) + "\n", { mode: 384, flag: "wx" });
|
|
20756
|
+
fs12.renameSync(tmp, googleCalendarPath());
|
|
20717
20757
|
} catch {
|
|
20718
20758
|
throw new Error("The Google Calendar sign-in could not be saved privately on this machine. Nothing new was saved.");
|
|
20719
20759
|
} finally {
|
|
20720
20760
|
if (tmp) {
|
|
20721
20761
|
try {
|
|
20722
|
-
|
|
20762
|
+
fs12.unlinkSync(tmp);
|
|
20723
20763
|
} catch {
|
|
20724
20764
|
}
|
|
20725
20765
|
}
|
|
@@ -20727,20 +20767,20 @@ function saveCalendarSecret(secret) {
|
|
|
20727
20767
|
}
|
|
20728
20768
|
function removeCalendarSecret() {
|
|
20729
20769
|
try {
|
|
20730
|
-
|
|
20770
|
+
fs12.unlinkSync(googleCalendarPath());
|
|
20731
20771
|
return true;
|
|
20732
20772
|
} catch (error) {
|
|
20733
20773
|
if (error.code === "ENOENT") return false;
|
|
20734
20774
|
throw new Error("The Google Calendar sign-in could not be removed from this machine.");
|
|
20735
20775
|
}
|
|
20736
20776
|
}
|
|
20737
|
-
var
|
|
20777
|
+
var fs12, path11, import_node_crypto4, import_zod51, CALENDAR_SCOPE, SecretSchema, googleCalendarPath;
|
|
20738
20778
|
var init_google_calendar_store = __esm({
|
|
20739
20779
|
"src/command-os/connectors/google-calendar-store.ts"() {
|
|
20740
20780
|
"use strict";
|
|
20741
|
-
|
|
20742
|
-
|
|
20743
|
-
|
|
20781
|
+
fs12 = __toESM(require("node:fs"));
|
|
20782
|
+
path11 = __toESM(require("node:path"));
|
|
20783
|
+
import_node_crypto4 = require("node:crypto");
|
|
20744
20784
|
import_zod51 = require("zod");
|
|
20745
20785
|
init_credentials_store();
|
|
20746
20786
|
CALENDAR_SCOPE = "https://www.googleapis.com/auth/calendar.readonly";
|
|
@@ -20751,7 +20791,7 @@ var init_google_calendar_store = __esm({
|
|
|
20751
20791
|
scope: import_zod51.z.literal(CALENDAR_SCOPE),
|
|
20752
20792
|
connectedAt: import_zod51.z.string()
|
|
20753
20793
|
});
|
|
20754
|
-
googleCalendarPath = () =>
|
|
20794
|
+
googleCalendarPath = () => path11.join(appDataDir(), "google-calendar.json");
|
|
20755
20795
|
}
|
|
20756
20796
|
});
|
|
20757
20797
|
|
|
@@ -20873,16 +20913,16 @@ function createGoogleCalendar(deps = {}) {
|
|
|
20873
20913
|
requireOnlyCalendar(result.scope);
|
|
20874
20914
|
return result.access_token;
|
|
20875
20915
|
}
|
|
20876
|
-
async function pages(
|
|
20916
|
+
async function pages(path41, access, item, query = {}) {
|
|
20877
20917
|
const result = [];
|
|
20878
20918
|
const seen = /* @__PURE__ */ new Set();
|
|
20879
20919
|
let pageToken;
|
|
20880
20920
|
for (let page = 0; page < 100; page++) {
|
|
20881
|
-
const url = new URL(`${API}${
|
|
20921
|
+
const url = new URL(`${API}${path41}`);
|
|
20882
20922
|
for (const [k, v] of Object.entries(query)) url.searchParams.set(k, v);
|
|
20883
20923
|
if (pageToken) url.searchParams.set("pageToken", pageToken);
|
|
20884
20924
|
const data = parsed(
|
|
20885
|
-
import_zod52.z.object({ items: import_zod52.z.array(item).optional(), nextPageToken: import_zod52.z.string().optional(), kind: import_zod52.z.string().optional() }).refine((p) => p.items !== void 0 || p.kind === (
|
|
20925
|
+
import_zod52.z.object({ items: import_zod52.z.array(item).optional(), nextPageToken: import_zod52.z.string().optional(), kind: import_zod52.z.string().optional() }).refine((p) => p.items !== void 0 || p.kind === (path41.endsWith("/events") ? "calendar#events" : "calendar#calendarList")),
|
|
20886
20926
|
await request(url.href, { headers: { Authorization: `Bearer ${access}` } })
|
|
20887
20927
|
);
|
|
20888
20928
|
result.push(...data.items ?? []);
|
|
@@ -20900,8 +20940,8 @@ function createGoogleCalendar(deps = {}) {
|
|
|
20900
20940
|
async function start() {
|
|
20901
20941
|
const app = appConfig();
|
|
20902
20942
|
clearPending2();
|
|
20903
|
-
const state = (0,
|
|
20904
|
-
const verifier = (0,
|
|
20943
|
+
const state = (0, import_node_crypto5.randomBytes)(32).toString("base64url");
|
|
20944
|
+
const verifier = (0, import_node_crypto5.randomBytes)(32).toString("base64url");
|
|
20905
20945
|
const server2 = (deps.createServer ?? import_node_http.createServer)((req, res) => {
|
|
20906
20946
|
res.setHeader("Content-Type", "text/plain; charset=utf-8");
|
|
20907
20947
|
res.setHeader("Cache-Control", "no-store");
|
|
@@ -20955,7 +20995,7 @@ function createGoogleCalendar(deps = {}) {
|
|
|
20955
20995
|
response_type: "code",
|
|
20956
20996
|
scope: CALENDAR_SCOPE,
|
|
20957
20997
|
state,
|
|
20958
|
-
code_challenge: (0,
|
|
20998
|
+
code_challenge: (0, import_node_crypto5.createHash)("sha256").update(verifier).digest("base64url"),
|
|
20959
20999
|
code_challenge_method: "S256",
|
|
20960
21000
|
access_type: "offline",
|
|
20961
21001
|
prompt: "consent"
|
|
@@ -21085,13 +21125,13 @@ function createGoogleCalendar(deps = {}) {
|
|
|
21085
21125
|
close: clearPending2
|
|
21086
21126
|
};
|
|
21087
21127
|
}
|
|
21088
|
-
var import_node_http,
|
|
21128
|
+
var import_node_http, import_node_crypto5, import_node_child_process, import_zod52, TOKEN_URL, REVOKE_URL, API, tokenSchema, calendarSchema, eventSchema;
|
|
21089
21129
|
var init_google_calendar = __esm({
|
|
21090
21130
|
"src/command-os/connectors/google-calendar.ts"() {
|
|
21091
21131
|
"use strict";
|
|
21092
21132
|
init_any_signal();
|
|
21093
21133
|
import_node_http = require("node:http");
|
|
21094
|
-
|
|
21134
|
+
import_node_crypto5 = require("node:crypto");
|
|
21095
21135
|
import_node_child_process = require("node:child_process");
|
|
21096
21136
|
import_zod52 = require("zod");
|
|
21097
21137
|
init_credentials_store();
|
|
@@ -21133,8 +21173,8 @@ function redactConnectionKey(value, key) {
|
|
|
21133
21173
|
return value;
|
|
21134
21174
|
}
|
|
21135
21175
|
function stripeFetcher(key, signal) {
|
|
21136
|
-
return async (
|
|
21137
|
-
const url = new URL(`https://api.stripe.com${
|
|
21176
|
+
return async (path41, query) => {
|
|
21177
|
+
const url = new URL(`https://api.stripe.com${path41}`);
|
|
21138
21178
|
for (const [k, v] of Object.entries(query ?? {})) url.searchParams.set(k, v);
|
|
21139
21179
|
const res = await fetch(url, { headers: { Authorization: `Bearer ${key}` }, signal });
|
|
21140
21180
|
if (!res.ok) {
|
|
@@ -21179,7 +21219,7 @@ async function connectSystem({ system, key, label }) {
|
|
|
21179
21219
|
system,
|
|
21180
21220
|
because: h.detail,
|
|
21181
21221
|
storedAt: null,
|
|
21182
|
-
note: "Nothing was saved, so nothing now claims to be connected. Fix the key and run this again \u2014 a Stripe RESTRICTED key needs read access to subscriptions, invoices and customers."
|
|
21222
|
+
note: "Nothing was saved, so nothing now claims to be connected. Fix the key and run this again \u2014 a Stripe RESTRICTED key needs read access to subscriptions, invoices and customers (and charges and products for the money figures)."
|
|
21183
21223
|
};
|
|
21184
21224
|
}
|
|
21185
21225
|
const s = connect(system, key, label, void 0, h.state === "WORKING");
|
|
@@ -21223,11 +21263,11 @@ var init_handlers = __esm({
|
|
|
21223
21263
|
});
|
|
21224
21264
|
|
|
21225
21265
|
// src/command-os/connectors/stripe-read.ts
|
|
21226
|
-
async function listAll(f,
|
|
21266
|
+
async function listAll(f, path41, schema, extra = {}) {
|
|
21227
21267
|
const out = [];
|
|
21228
21268
|
let after;
|
|
21229
21269
|
for (let page = 0; page < 20; page++) {
|
|
21230
|
-
const result = import_zod53.z.object({ data: import_zod53.z.array(schema), has_more: import_zod53.z.boolean().optional() }).parse(await f(
|
|
21270
|
+
const result = import_zod53.z.object({ data: import_zod53.z.array(schema), has_more: import_zod53.z.boolean().optional() }).parse(await f(path41, { limit: "100", ...extra, ...after ? { starting_after: after } : {} }));
|
|
21231
21271
|
out.push(...result.data);
|
|
21232
21272
|
if (!result.has_more) return out;
|
|
21233
21273
|
const last = import_zod53.z.object({ id: import_zod53.z.string().min(1) }).parse(result.data.at(-1));
|
|
@@ -21244,7 +21284,15 @@ async function readStripe(f) {
|
|
|
21244
21284
|
]);
|
|
21245
21285
|
return { subs, invoices, customers };
|
|
21246
21286
|
}
|
|
21247
|
-
|
|
21287
|
+
async function readStripeMoney(f, now) {
|
|
21288
|
+
const since = String(Math.floor(now.getTime() / 1e3) - 30 * 86400);
|
|
21289
|
+
const [charges, products] = await Promise.all([
|
|
21290
|
+
listAll(f, "/v1/charges", Charge, { "created[gte]": since }),
|
|
21291
|
+
listAll(f, "/v1/products", Product)
|
|
21292
|
+
]);
|
|
21293
|
+
return { charges, products };
|
|
21294
|
+
}
|
|
21295
|
+
var import_zod53, Customer, Subscription, Invoice, Charge, Product;
|
|
21248
21296
|
var init_stripe_read = __esm({
|
|
21249
21297
|
"src/command-os/connectors/stripe-read.ts"() {
|
|
21250
21298
|
"use strict";
|
|
@@ -21261,9 +21309,10 @@ var init_stripe_read = __esm({
|
|
|
21261
21309
|
quantity: import_zod53.z.number().nullable().optional(),
|
|
21262
21310
|
current_period_end: import_zod53.z.number().optional(),
|
|
21263
21311
|
price: import_zod53.z.object({
|
|
21312
|
+
product: import_zod53.z.string().nullable().optional(),
|
|
21264
21313
|
unit_amount: import_zod53.z.number().nullable().optional(),
|
|
21265
21314
|
currency: import_zod53.z.string().nullable().optional(),
|
|
21266
|
-
recurring: import_zod53.z.object({ interval: import_zod53.z.string().optional() }).nullable().optional()
|
|
21315
|
+
recurring: import_zod53.z.object({ interval: import_zod53.z.string().optional(), interval_count: import_zod53.z.number().optional() }).nullable().optional()
|
|
21267
21316
|
}).nullable().optional()
|
|
21268
21317
|
})).optional() }).optional()
|
|
21269
21318
|
});
|
|
@@ -21276,15 +21325,28 @@ var init_stripe_read = __esm({
|
|
|
21276
21325
|
amount_remaining: import_zod53.z.number().optional(),
|
|
21277
21326
|
due_date: import_zod53.z.number().nullable().optional(),
|
|
21278
21327
|
number: import_zod53.z.string().nullable().optional(),
|
|
21279
|
-
hosted_invoice_url: import_zod53.z.string().nullable().optional()
|
|
21328
|
+
hosted_invoice_url: import_zod53.z.string().nullable().optional(),
|
|
21329
|
+
amount_paid: import_zod53.z.number().optional(),
|
|
21330
|
+
subscription: import_zod53.z.string().nullable().optional(),
|
|
21331
|
+
parent: import_zod53.z.object({ subscription_details: import_zod53.z.object({ subscription: import_zod53.z.string().nullable().optional() }).nullable().optional() }).nullable().optional(),
|
|
21332
|
+
status_transitions: import_zod53.z.object({ paid_at: import_zod53.z.number().nullable().optional() }).nullable().optional()
|
|
21333
|
+
});
|
|
21334
|
+
Charge = import_zod53.z.object({
|
|
21335
|
+
id: import_zod53.z.string(),
|
|
21336
|
+
amount: import_zod53.z.number(),
|
|
21337
|
+
amount_refunded: import_zod53.z.number().optional(),
|
|
21338
|
+
currency: import_zod53.z.string(),
|
|
21339
|
+
status: import_zod53.z.string(),
|
|
21340
|
+
created: import_zod53.z.number()
|
|
21280
21341
|
});
|
|
21342
|
+
Product = import_zod53.z.object({ id: import_zod53.z.string(), name: import_zod53.z.string() });
|
|
21281
21343
|
}
|
|
21282
21344
|
});
|
|
21283
21345
|
|
|
21284
21346
|
// src/client-engagements.ts
|
|
21285
21347
|
function clientEngagementsPath() {
|
|
21286
21348
|
const override = process.env[CLIENT_ENGAGEMENTS_ENV];
|
|
21287
|
-
return override && override.trim() ? override :
|
|
21349
|
+
return override && override.trim() ? override : path12.join(appDataDir(), "client-engagements.json");
|
|
21288
21350
|
}
|
|
21289
21351
|
function isAgencyClient(e) {
|
|
21290
21352
|
return (e.kind ?? DEFAULT_ENGAGEMENT_KIND) === "client";
|
|
@@ -21343,7 +21405,7 @@ function sanitizeEngagements(raw) {
|
|
|
21343
21405
|
}
|
|
21344
21406
|
function readClientEngagements(file2 = clientEngagementsPath()) {
|
|
21345
21407
|
try {
|
|
21346
|
-
const raw = JSON.parse(
|
|
21408
|
+
const raw = JSON.parse(fs13.readFileSync(file2, "utf8"));
|
|
21347
21409
|
if (raw && typeof raw === "object") return sanitizeEngagements(raw);
|
|
21348
21410
|
} catch {
|
|
21349
21411
|
}
|
|
@@ -21370,7 +21432,7 @@ function writeClientEngagement(locationId2, patch, file2 = clientEngagementsPath
|
|
|
21370
21432
|
if (!id || FORBIDDEN_KEY.has(id)) throw new Error("An id is required.");
|
|
21371
21433
|
const stamp = () => {
|
|
21372
21434
|
try {
|
|
21373
|
-
return
|
|
21435
|
+
return fs13.readFileSync(file2, "utf8");
|
|
21374
21436
|
} catch {
|
|
21375
21437
|
return "";
|
|
21376
21438
|
}
|
|
@@ -21390,20 +21452,20 @@ function writeClientEngagement(locationId2, patch, file2 = clientEngagementsPath
|
|
|
21390
21452
|
};
|
|
21391
21453
|
const persist = (next) => {
|
|
21392
21454
|
if (file2 === clientEngagementsPath() && !process.env[CLIENT_ENGAGEMENTS_ENV]) ensureAppDataDir();
|
|
21393
|
-
|
|
21455
|
+
fs13.mkdirSync(path12.dirname(file2), { recursive: true });
|
|
21394
21456
|
const tmp = `${file2}.tmp.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}`;
|
|
21395
21457
|
try {
|
|
21396
|
-
|
|
21458
|
+
fs13.writeFileSync(tmp, JSON.stringify(next, null, 2) + "\n", "utf-8");
|
|
21397
21459
|
if (process.platform !== "win32") {
|
|
21398
21460
|
try {
|
|
21399
|
-
|
|
21461
|
+
fs13.chmodSync(tmp, 384);
|
|
21400
21462
|
} catch {
|
|
21401
21463
|
}
|
|
21402
21464
|
}
|
|
21403
|
-
|
|
21465
|
+
fs13.renameSync(tmp, file2);
|
|
21404
21466
|
} catch (e) {
|
|
21405
21467
|
try {
|
|
21406
|
-
|
|
21468
|
+
fs13.unlinkSync(tmp);
|
|
21407
21469
|
} catch {
|
|
21408
21470
|
}
|
|
21409
21471
|
throw e;
|
|
@@ -21475,12 +21537,12 @@ function monthlyFloor(roster, profile) {
|
|
|
21475
21537
|
byKind
|
|
21476
21538
|
};
|
|
21477
21539
|
}
|
|
21478
|
-
var
|
|
21540
|
+
var fs13, path12, CLIENT_ENGAGEMENTS_ENV, ENGAGEMENT_STATUSES, ENGAGEMENT_KINDS, DEFAULT_ENGAGEMENT_KIND, EMPTY2, MAX_NOTES, MAX_SOP2, MAX_CLIENTS, FORBIDDEN_KEY;
|
|
21479
21541
|
var init_client_engagements = __esm({
|
|
21480
21542
|
"src/client-engagements.ts"() {
|
|
21481
21543
|
"use strict";
|
|
21482
|
-
|
|
21483
|
-
|
|
21544
|
+
fs13 = __toESM(require("fs"));
|
|
21545
|
+
path12 = __toESM(require("path"));
|
|
21484
21546
|
init_credentials_store();
|
|
21485
21547
|
init_field_sanitize();
|
|
21486
21548
|
CLIENT_ENGAGEMENTS_ENV = "GHL_MCP_CLIENT_ENGAGEMENTS";
|
|
@@ -21498,7 +21560,7 @@ var init_client_engagements = __esm({
|
|
|
21498
21560
|
// src/board-roster.ts
|
|
21499
21561
|
function readBoardState() {
|
|
21500
21562
|
try {
|
|
21501
|
-
const raw = JSON.parse(
|
|
21563
|
+
const raw = JSON.parse(fs14.readFileSync(path13.join(appDataDir(), "cockpit-state.json"), "utf8"));
|
|
21502
21564
|
if (raw && typeof raw === "object" && raw.clients && typeof raw.clients === "object") return raw;
|
|
21503
21565
|
} catch {
|
|
21504
21566
|
}
|
|
@@ -21516,12 +21578,12 @@ function knownClients(registry2, state) {
|
|
|
21516
21578
|
}
|
|
21517
21579
|
return [...out.values()];
|
|
21518
21580
|
}
|
|
21519
|
-
var
|
|
21581
|
+
var fs14, path13;
|
|
21520
21582
|
var init_board_roster = __esm({
|
|
21521
21583
|
"src/board-roster.ts"() {
|
|
21522
21584
|
"use strict";
|
|
21523
|
-
|
|
21524
|
-
|
|
21585
|
+
fs14 = __toESM(require("fs"));
|
|
21586
|
+
path13 = __toESM(require("path"));
|
|
21525
21587
|
init_credentials_store();
|
|
21526
21588
|
init_client_engagements();
|
|
21527
21589
|
}
|
|
@@ -21736,17 +21798,17 @@ var init_stage_boundary = __esm({
|
|
|
21736
21798
|
// src/plan-guide.ts
|
|
21737
21799
|
function skillRoot() {
|
|
21738
21800
|
const candidates = [
|
|
21739
|
-
|
|
21801
|
+
path14.join(__dirname, "..", "skills", "blueprint"),
|
|
21740
21802
|
// dist/ → package root
|
|
21741
|
-
|
|
21803
|
+
path14.join(__dirname, "..", "..", "skills", "blueprint"),
|
|
21742
21804
|
// src/ in dev
|
|
21743
|
-
|
|
21805
|
+
path14.join(process.cwd(), "skills", "blueprint")
|
|
21744
21806
|
];
|
|
21745
|
-
return candidates.find((c) =>
|
|
21807
|
+
return candidates.find((c) => fs15.existsSync(path14.join(c, "references", "build-plan-schema.md"))) ?? null;
|
|
21746
21808
|
}
|
|
21747
21809
|
function readIf(p, max) {
|
|
21748
21810
|
try {
|
|
21749
|
-
const t =
|
|
21811
|
+
const t = fs15.readFileSync(p, "utf8");
|
|
21750
21812
|
return t.length > max ? t.slice(0, max) : t;
|
|
21751
21813
|
} catch {
|
|
21752
21814
|
return null;
|
|
@@ -21759,12 +21821,12 @@ function presetForIndustry(industry) {
|
|
|
21759
21821
|
function loadPlanGuide(industry) {
|
|
21760
21822
|
const root = skillRoot();
|
|
21761
21823
|
if (!root) return "";
|
|
21762
|
-
const schema = readIf(
|
|
21824
|
+
const schema = readIf(path14.join(root, "references", "build-plan-schema.md"), CAP_SCHEMA);
|
|
21763
21825
|
const presetName = presetForIndustry(industry);
|
|
21764
|
-
const preset = readIf(
|
|
21826
|
+
const preset = readIf(path14.join(root, "presets", `${presetName}.preset.json`), CAP_PRESET);
|
|
21765
21827
|
const exampleFile = EXAMPLE_FOR_PRESET[presetName] ?? "sample-build-plan.json";
|
|
21766
|
-
const example = readIf(
|
|
21767
|
-
const copyGuide = readIf(
|
|
21828
|
+
const example = readIf(path14.join(root, "examples", exampleFile), CAP_EXAMPLE);
|
|
21829
|
+
const copyGuide = readIf(path14.join(root, "references", "copy-guide.md"), CAP_COPY_GUIDE);
|
|
21768
21830
|
if (!schema) return "";
|
|
21769
21831
|
return `
|
|
21770
21832
|
PLAN GUIDE \u2014 everything you need to write the build plan is below. Do NOT try to read files; you cannot.
|
|
@@ -21782,12 +21844,12 @@ ${example}
|
|
|
21782
21844
|
` : "") + `=== End of plan guide ===
|
|
21783
21845
|
`;
|
|
21784
21846
|
}
|
|
21785
|
-
var
|
|
21847
|
+
var fs15, path14, PRESET_FOR_INDUSTRY, EXAMPLE_FOR_PRESET, COPY_INSTRUCTION, CALENDAR_INSTRUCTION, CAP_SCHEMA, CAP_PRESET, CAP_EXAMPLE, CAP_COPY_GUIDE;
|
|
21786
21848
|
var init_plan_guide = __esm({
|
|
21787
21849
|
"src/plan-guide.ts"() {
|
|
21788
21850
|
"use strict";
|
|
21789
|
-
|
|
21790
|
-
|
|
21851
|
+
fs15 = __toESM(require("fs"));
|
|
21852
|
+
path14 = __toESM(require("path"));
|
|
21791
21853
|
PRESET_FOR_INDUSTRY = {
|
|
21792
21854
|
"med-spa": "med-spa",
|
|
21793
21855
|
clinic: "clinic",
|
|
@@ -22373,26 +22435,26 @@ function isHandoffAction(a, nurtureTriggerTags) {
|
|
|
22373
22435
|
function collectDefinedRefs(plan) {
|
|
22374
22436
|
const refs = /* @__PURE__ */ new Map();
|
|
22375
22437
|
const duplicates = [];
|
|
22376
|
-
const
|
|
22438
|
+
const add2 = (ref) => {
|
|
22377
22439
|
if (refs.has(ref)) duplicates.push(ref);
|
|
22378
22440
|
else refs.set(ref, refNamespace(ref));
|
|
22379
22441
|
};
|
|
22380
|
-
for (const u of plan.users ?? [])
|
|
22442
|
+
for (const u of plan.users ?? []) add2(u.ref);
|
|
22381
22443
|
for (const p of plan.pipelines ?? []) {
|
|
22382
|
-
|
|
22383
|
-
for (const s of p.stages)
|
|
22384
|
-
}
|
|
22385
|
-
for (const f of plan.customFields ?? [])
|
|
22386
|
-
for (const t of plan.tags ?? [])
|
|
22387
|
-
for (const cv of plan.customValues ?? [])
|
|
22388
|
-
for (const c of plan.calendars ?? [])
|
|
22389
|
-
for (const fm of plan.forms ?? [])
|
|
22390
|
-
for (const e of plan.emails ?? [])
|
|
22391
|
-
for (const s of plan.sms ?? [])
|
|
22392
|
-
for (const t of plan.templates?.emails ?? [])
|
|
22393
|
-
for (const t of plan.templates?.sms ?? [])
|
|
22394
|
-
for (const w of plan.workflows ?? [])
|
|
22395
|
-
for (const h of plan.handoffs ?? [])
|
|
22444
|
+
add2(p.ref);
|
|
22445
|
+
for (const s of p.stages) add2(s.ref);
|
|
22446
|
+
}
|
|
22447
|
+
for (const f of plan.customFields ?? []) add2(f.ref);
|
|
22448
|
+
for (const t of plan.tags ?? []) add2(t.ref);
|
|
22449
|
+
for (const cv of plan.customValues ?? []) add2(cv.ref);
|
|
22450
|
+
for (const c of plan.calendars ?? []) add2(c.ref);
|
|
22451
|
+
for (const fm of plan.forms ?? []) add2(fm.ref);
|
|
22452
|
+
for (const e of plan.emails ?? []) add2(e.ref);
|
|
22453
|
+
for (const s of plan.sms ?? []) add2(s.ref);
|
|
22454
|
+
for (const t of plan.templates?.emails ?? []) add2(t.ref);
|
|
22455
|
+
for (const t of plan.templates?.sms ?? []) add2(t.ref);
|
|
22456
|
+
for (const w of plan.workflows ?? []) add2(w.ref);
|
|
22457
|
+
for (const h of plan.handoffs ?? []) add2(h.ref);
|
|
22396
22458
|
return { refs, duplicates };
|
|
22397
22459
|
}
|
|
22398
22460
|
function checkRefIntegrity(plan, defined) {
|
|
@@ -24407,10 +24469,10 @@ function progressLine(e) {
|
|
|
24407
24469
|
}
|
|
24408
24470
|
}
|
|
24409
24471
|
function defaultProgressDir(base = appDataDir()) {
|
|
24410
|
-
return
|
|
24472
|
+
return path15.join(base, "progress");
|
|
24411
24473
|
}
|
|
24412
24474
|
function progressFilePath(dir, locationId2) {
|
|
24413
|
-
return
|
|
24475
|
+
return path15.join(dir, `${locationId2.replace(/[^A-Za-z0-9_-]/g, "_")}.json`);
|
|
24414
24476
|
}
|
|
24415
24477
|
function createProgressFileSink(locationId2, plan, dir = process.env[PROGRESS_DIR_ENV]) {
|
|
24416
24478
|
if (!dir) return null;
|
|
@@ -24418,10 +24480,10 @@ function createProgressFileSink(locationId2, plan, dir = process.env[PROGRESS_DI
|
|
|
24418
24480
|
const file2 = progressFilePath(dir, locationId2);
|
|
24419
24481
|
const write = () => {
|
|
24420
24482
|
try {
|
|
24421
|
-
|
|
24483
|
+
fs16.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
24422
24484
|
const tmp = `${file2}.tmp`;
|
|
24423
|
-
|
|
24424
|
-
|
|
24485
|
+
fs16.writeFileSync(tmp, JSON.stringify({ locationId: locationId2, progress: tracker.snapshot() }), { mode: 384 });
|
|
24486
|
+
fs16.renameSync(tmp, file2);
|
|
24425
24487
|
} catch {
|
|
24426
24488
|
}
|
|
24427
24489
|
};
|
|
@@ -24439,7 +24501,7 @@ function createProgressFileSink(locationId2, plan, dir = process.env[PROGRESS_DI
|
|
|
24439
24501
|
}
|
|
24440
24502
|
function readProgressSnapshot(dir, locationId2) {
|
|
24441
24503
|
try {
|
|
24442
|
-
const raw = JSON.parse(
|
|
24504
|
+
const raw = JSON.parse(fs16.readFileSync(progressFilePath(dir, locationId2), "utf8"));
|
|
24443
24505
|
const p = raw?.progress;
|
|
24444
24506
|
if (!p || !Array.isArray(p.groups) || typeof p.updatedAt !== "string") return null;
|
|
24445
24507
|
return p;
|
|
@@ -24468,12 +24530,12 @@ function tapPush(arr, onItem) {
|
|
|
24468
24530
|
};
|
|
24469
24531
|
Object.defineProperty(arr, "push", { value: tapped, writable: true, configurable: true, enumerable: false });
|
|
24470
24532
|
}
|
|
24471
|
-
var
|
|
24533
|
+
var fs16, path15, PROGRESS_GROUPS, GROUP_INDEX, TYPE_TO_GROUP, HALT_TYPE_TO_GROUP, CREATING_LINE, BuildProgressTracker, PROGRESS_DIR_ENV;
|
|
24472
24534
|
var init_build_progress = __esm({
|
|
24473
24535
|
"src/build-progress.ts"() {
|
|
24474
24536
|
"use strict";
|
|
24475
|
-
|
|
24476
|
-
|
|
24537
|
+
fs16 = __toESM(require("fs"));
|
|
24538
|
+
path15 = __toESM(require("path"));
|
|
24477
24539
|
init_executor();
|
|
24478
24540
|
init_credentials_store();
|
|
24479
24541
|
PROGRESS_GROUPS = [
|
|
@@ -24528,11 +24590,11 @@ var init_build_progress = __esm({
|
|
|
24528
24590
|
current;
|
|
24529
24591
|
updatedAt;
|
|
24530
24592
|
constructor(plan) {
|
|
24531
|
-
const
|
|
24593
|
+
const totals2 = planProgressTotals(plan);
|
|
24532
24594
|
this.tallies = PROGRESS_GROUPS.map(({ key, label }) => ({
|
|
24533
24595
|
key,
|
|
24534
24596
|
label,
|
|
24535
|
-
total:
|
|
24597
|
+
total: totals2[key],
|
|
24536
24598
|
done: 0,
|
|
24537
24599
|
deferred: 0,
|
|
24538
24600
|
failed: 0,
|
|
@@ -24611,10 +24673,10 @@ var init_build_progress = __esm({
|
|
|
24611
24673
|
|
|
24612
24674
|
// src/build-journal.ts
|
|
24613
24675
|
function journalDir(base = appDataDir()) {
|
|
24614
|
-
return
|
|
24676
|
+
return path16.join(base, "build-journal");
|
|
24615
24677
|
}
|
|
24616
24678
|
function journalPath(locationId2, base) {
|
|
24617
|
-
return
|
|
24679
|
+
return path16.join(journalDir(base), `${locationId2}.jsonl`);
|
|
24618
24680
|
}
|
|
24619
24681
|
function newRunId(now = /* @__PURE__ */ new Date(), rand = defaultRand) {
|
|
24620
24682
|
const iso2 = now.toISOString();
|
|
@@ -24688,40 +24750,40 @@ function directoryFlushUnsupported(e) {
|
|
|
24688
24750
|
}
|
|
24689
24751
|
function appendJournal(record, base) {
|
|
24690
24752
|
const dir = journalDir(base);
|
|
24691
|
-
|
|
24753
|
+
fs17.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
24692
24754
|
const file2 = journalPath(record.locationId, base);
|
|
24693
24755
|
let needsBoundary = false;
|
|
24694
24756
|
try {
|
|
24695
|
-
const st =
|
|
24757
|
+
const st = fs17.statSync(file2);
|
|
24696
24758
|
if (st.size > 0) {
|
|
24697
|
-
const fd2 =
|
|
24759
|
+
const fd2 = fs17.openSync(file2, "r");
|
|
24698
24760
|
try {
|
|
24699
24761
|
const buf = Buffer.alloc(1);
|
|
24700
|
-
|
|
24762
|
+
fs17.readSync(fd2, buf, 0, 1, st.size - 1);
|
|
24701
24763
|
needsBoundary = buf[0] !== 10;
|
|
24702
24764
|
} finally {
|
|
24703
|
-
|
|
24765
|
+
fs17.closeSync(fd2);
|
|
24704
24766
|
}
|
|
24705
24767
|
}
|
|
24706
24768
|
} catch {
|
|
24707
24769
|
}
|
|
24708
|
-
const fd =
|
|
24770
|
+
const fd = fs17.openSync(file2, "a", 384);
|
|
24709
24771
|
try {
|
|
24710
|
-
|
|
24772
|
+
fs17.appendFileSync(fd, `${needsBoundary ? "\n" : ""}${JSON.stringify(record)}
|
|
24711
24773
|
`);
|
|
24712
|
-
|
|
24774
|
+
fs17.fsyncSync(fd);
|
|
24713
24775
|
} finally {
|
|
24714
|
-
|
|
24776
|
+
fs17.closeSync(fd);
|
|
24715
24777
|
}
|
|
24716
|
-
for (const directory of [dir,
|
|
24778
|
+
for (const directory of [dir, path16.dirname(dir)]) {
|
|
24717
24779
|
let dirFd;
|
|
24718
24780
|
try {
|
|
24719
|
-
dirFd =
|
|
24720
|
-
|
|
24781
|
+
dirFd = fs17.openSync(directory, "r");
|
|
24782
|
+
fs17.fsyncSync(dirFd);
|
|
24721
24783
|
} catch (e) {
|
|
24722
24784
|
if (!directoryFlushUnsupported(e)) throw e;
|
|
24723
24785
|
} finally {
|
|
24724
|
-
if (dirFd !== void 0)
|
|
24786
|
+
if (dirFd !== void 0) fs17.closeSync(dirFd);
|
|
24725
24787
|
}
|
|
24726
24788
|
}
|
|
24727
24789
|
return file2;
|
|
@@ -24729,7 +24791,7 @@ function appendJournal(record, base) {
|
|
|
24729
24791
|
function readJournal(locationId2, base) {
|
|
24730
24792
|
let raw;
|
|
24731
24793
|
try {
|
|
24732
|
-
raw =
|
|
24794
|
+
raw = fs17.readFileSync(journalPath(locationId2, base), "utf8");
|
|
24733
24795
|
} catch {
|
|
24734
24796
|
return [];
|
|
24735
24797
|
}
|
|
@@ -24800,12 +24862,12 @@ function describeRuns(locationId2, base) {
|
|
|
24800
24862
|
reverted: reverted.has(r.runId)
|
|
24801
24863
|
})).reverse();
|
|
24802
24864
|
}
|
|
24803
|
-
var
|
|
24865
|
+
var fs17, path16, DIRECTORY_FLUSH_UNSUPPORTED;
|
|
24804
24866
|
var init_build_journal = __esm({
|
|
24805
24867
|
"src/build-journal.ts"() {
|
|
24806
24868
|
"use strict";
|
|
24807
|
-
|
|
24808
|
-
|
|
24869
|
+
fs17 = __toESM(require("fs"));
|
|
24870
|
+
path16 = __toESM(require("path"));
|
|
24809
24871
|
init_credentials_store();
|
|
24810
24872
|
DIRECTORY_FLUSH_UNSUPPORTED = /* @__PURE__ */ new Set(["EPERM", "EACCES", "EINVAL", "ENOTSUP", "EOPNOTSUPP", "ENOSYS", "EISDIR", "EBADF"]);
|
|
24811
24873
|
}
|
|
@@ -24817,7 +24879,7 @@ function unusableShimMessage(shim) {
|
|
|
24817
24879
|
}
|
|
24818
24880
|
function runnable(p) {
|
|
24819
24881
|
try {
|
|
24820
|
-
|
|
24882
|
+
fs18.accessSync(p, fs18.constants.X_OK);
|
|
24821
24883
|
return true;
|
|
24822
24884
|
} catch {
|
|
24823
24885
|
return false;
|
|
@@ -24829,27 +24891,27 @@ function claudeFileNames(platform2, env2) {
|
|
|
24829
24891
|
return [...exts.map((ext) => `claude${ext}`), "claude"];
|
|
24830
24892
|
}
|
|
24831
24893
|
function isShim(file2) {
|
|
24832
|
-
const ext =
|
|
24894
|
+
const ext = path17.extname(file2).toLowerCase();
|
|
24833
24895
|
return ext === ".cmd" || ext === ".bat";
|
|
24834
24896
|
}
|
|
24835
24897
|
function shimTarget(shim) {
|
|
24836
24898
|
let text2;
|
|
24837
24899
|
try {
|
|
24838
|
-
text2 =
|
|
24900
|
+
text2 = fs18.readFileSync(shim, "utf8");
|
|
24839
24901
|
} catch {
|
|
24840
24902
|
return null;
|
|
24841
24903
|
}
|
|
24842
24904
|
const refs = [...text2.matchAll(/"%~?dp0%?\\([^"\r\n]+)"/g)].map((m) => m[1]);
|
|
24843
|
-
const resolve12 = (rel) =>
|
|
24905
|
+
const resolve12 = (rel) => path17.join(path17.dirname(shim), ...rel.split(/[\\/]/));
|
|
24844
24906
|
const script = refs.find((r) => /\.(?:js|cjs|mjs)$/i.test(r));
|
|
24845
24907
|
if (script) {
|
|
24846
24908
|
const p = resolve12(script);
|
|
24847
|
-
return
|
|
24909
|
+
return fs18.existsSync(p) ? { kind: "script", path: p } : null;
|
|
24848
24910
|
}
|
|
24849
24911
|
const program = refs.find((r) => /\.exe$/i.test(r) && !/(^|[\\/])node\.exe$/i.test(r));
|
|
24850
24912
|
if (program) {
|
|
24851
24913
|
const p = resolve12(program);
|
|
24852
|
-
return
|
|
24914
|
+
return fs18.existsSync(p) ? { kind: "program", path: p } : null;
|
|
24853
24915
|
}
|
|
24854
24916
|
return null;
|
|
24855
24917
|
}
|
|
@@ -24862,7 +24924,7 @@ function claudeBin(lookup = {}) {
|
|
|
24862
24924
|
let unusableShim = null;
|
|
24863
24925
|
for (const dir of dirs) {
|
|
24864
24926
|
for (const name of names) {
|
|
24865
|
-
const candidate =
|
|
24927
|
+
const candidate = path17.join(dir, name);
|
|
24866
24928
|
if (!runnable(candidate)) continue;
|
|
24867
24929
|
if (platform2 === "win32" && isShim(candidate) && !shimTarget(candidate)) {
|
|
24868
24930
|
unusableShim ??= candidate;
|
|
@@ -24871,7 +24933,7 @@ function claudeBin(lookup = {}) {
|
|
|
24871
24933
|
return candidate;
|
|
24872
24934
|
}
|
|
24873
24935
|
}
|
|
24874
|
-
const fallback =
|
|
24936
|
+
const fallback = path17.join(home, ".local", "bin", platform2 === "win32" ? "claude.exe" : "claude");
|
|
24875
24937
|
if (runnable(fallback)) return fallback;
|
|
24876
24938
|
throw new Error(unusableShim ? unusableShimMessage(unusableShim) : NOT_FOUND_MESSAGE);
|
|
24877
24939
|
}
|
|
@@ -24891,13 +24953,13 @@ function claudeCommand(bin, args, lookup = {}) {
|
|
|
24891
24953
|
if (!target) return { command: resolved2, args };
|
|
24892
24954
|
return target.kind === "script" ? { command: lookup.execPath ?? process.execPath, args: [target.path, ...args] } : { command: target.path, args };
|
|
24893
24955
|
}
|
|
24894
|
-
var
|
|
24956
|
+
var fs18, os3, path17, NOT_FOUND_MESSAGE;
|
|
24895
24957
|
var init_claude_locate = __esm({
|
|
24896
24958
|
"src/claude-locate.ts"() {
|
|
24897
24959
|
"use strict";
|
|
24898
|
-
|
|
24960
|
+
fs18 = __toESM(require("node:fs"));
|
|
24899
24961
|
os3 = __toESM(require("node:os"));
|
|
24900
|
-
|
|
24962
|
+
path17 = __toESM(require("node:path"));
|
|
24901
24963
|
NOT_FOUND_MESSAGE = "Claude Code CLI not found. Install it, then reopen the cockpit.";
|
|
24902
24964
|
}
|
|
24903
24965
|
});
|
|
@@ -24933,21 +24995,21 @@ function tooOldMessage(found) {
|
|
|
24933
24995
|
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.`;
|
|
24934
24996
|
}
|
|
24935
24997
|
function enginePath() {
|
|
24936
|
-
return
|
|
24998
|
+
return path18.join(appDataDir(), "command-os-engine.json");
|
|
24937
24999
|
}
|
|
24938
25000
|
function readEngineRecord(file2 = enginePath()) {
|
|
24939
25001
|
try {
|
|
24940
|
-
const r = JSON.parse(
|
|
25002
|
+
const r = JSON.parse(fs19.readFileSync(file2, "utf8"));
|
|
24941
25003
|
return typeof r?.version === "string" ? r : null;
|
|
24942
25004
|
} catch {
|
|
24943
25005
|
return null;
|
|
24944
25006
|
}
|
|
24945
25007
|
}
|
|
24946
25008
|
function writeEngineRecord(rec, file2 = enginePath()) {
|
|
24947
|
-
|
|
24948
|
-
|
|
25009
|
+
fs19.mkdirSync(path18.dirname(file2), { recursive: true });
|
|
25010
|
+
fs19.writeFileSync(file2, JSON.stringify(rec, null, 2), { mode: 384 });
|
|
24949
25011
|
try {
|
|
24950
|
-
|
|
25012
|
+
fs19.chmodSync(file2, 384);
|
|
24951
25013
|
} catch {
|
|
24952
25014
|
}
|
|
24953
25015
|
}
|
|
@@ -25086,13 +25148,13 @@ async function ensureEngineReady(bin, onNote = () => {
|
|
|
25086
25148
|
onNote(`Claude Code ${version} checked out${smoke.model ? ` (${smoke.model})` : ""}. Starting the step.`);
|
|
25087
25149
|
return { ok: true, version, smoked: true, smokeCostUsd: smoke.costUsd, smokeModel: smoke.model };
|
|
25088
25150
|
}
|
|
25089
|
-
var import_child_process2,
|
|
25151
|
+
var import_child_process2, fs19, path18, MIN_CLAUDE_VERSION, SMOKE_PROMPT;
|
|
25090
25152
|
var init_claude_cli = __esm({
|
|
25091
25153
|
"src/claude-cli.ts"() {
|
|
25092
25154
|
"use strict";
|
|
25093
25155
|
import_child_process2 = require("child_process");
|
|
25094
|
-
|
|
25095
|
-
|
|
25156
|
+
fs19 = __toESM(require("fs"));
|
|
25157
|
+
path18 = __toESM(require("path"));
|
|
25096
25158
|
init_credentials_store();
|
|
25097
25159
|
init_claude_locate();
|
|
25098
25160
|
MIN_CLAUDE_VERSION = "2.1.0";
|
|
@@ -25213,7 +25275,7 @@ function runStage(locationId2, stage, onEvent, opts = {}) {
|
|
|
25213
25275
|
const progressDir = opts.onProgress ? defaultProgressDir() : void 0;
|
|
25214
25276
|
if (progressDir) {
|
|
25215
25277
|
try {
|
|
25216
|
-
|
|
25278
|
+
fs20.rmSync(progressFilePath(progressDir, locationId2), { force: true });
|
|
25217
25279
|
} catch {
|
|
25218
25280
|
}
|
|
25219
25281
|
}
|
|
@@ -25327,13 +25389,13 @@ function runStage(locationId2, stage, onEvent, opts = {}) {
|
|
|
25327
25389
|
});
|
|
25328
25390
|
});
|
|
25329
25391
|
}
|
|
25330
|
-
var import_child_process3,
|
|
25392
|
+
var import_child_process3, fs20, STAFF_NOTIFICATION_RULE, MISSING_STEP_RULE, NO_INVENTED_CAUSE_RULE, STAGE_SPECS, HUMAN_GATE_STAGES;
|
|
25331
25393
|
var init_stage_runner = __esm({
|
|
25332
25394
|
"src/stage-runner.ts"() {
|
|
25333
25395
|
"use strict";
|
|
25334
25396
|
init_intake_notification();
|
|
25335
25397
|
import_child_process3 = require("child_process");
|
|
25336
|
-
|
|
25398
|
+
fs20 = __toESM(require("fs"));
|
|
25337
25399
|
init_stage_boundary();
|
|
25338
25400
|
init_plan_guide();
|
|
25339
25401
|
init_question_set();
|
|
@@ -28410,19 +28472,6 @@ async function readDailyBrief(registry2, options = {}) {
|
|
|
28410
28472
|
return { brief, ownDay, accounts: { total: roster.filter((r) => !r.pending).length, unread: liveRead?.unreachable?.length ?? 0 } };
|
|
28411
28473
|
}
|
|
28412
28474
|
function registerDailyBriefTools(server2, registry2) {
|
|
28413
|
-
const recordTool = server2.tool(
|
|
28414
|
-
"record_brief_answers",
|
|
28415
|
-
"Save up to 50 rows from your Gmail or Google Drive chat brief on this machine, one source per call. Replaces that source's previous rows; makes no provider calls. Record at most 50 rows per source.",
|
|
28416
|
-
BriefAnswersInput.shape,
|
|
28417
|
-
async (input) => {
|
|
28418
|
-
try {
|
|
28419
|
-
return jsonResponse(recordBriefAnswers(input));
|
|
28420
|
-
} catch (error) {
|
|
28421
|
-
return errorResponse(error);
|
|
28422
|
-
}
|
|
28423
|
-
}
|
|
28424
|
-
);
|
|
28425
|
-
if (recordTool) recordTool.inputSchema = BriefAnswersInput;
|
|
28426
28475
|
safeTool(
|
|
28427
28476
|
server2,
|
|
28428
28477
|
"daily_brief",
|
|
@@ -28434,7 +28483,7 @@ function registerDailyBriefTools(server2, registry2) {
|
|
|
28434
28483
|
},
|
|
28435
28484
|
async ({ live, hoursBack, quietAfterDays }) => {
|
|
28436
28485
|
const { brief } = await readDailyBrief(registry2, { live, hoursBack, quietAfterDays });
|
|
28437
|
-
return
|
|
28486
|
+
return brief;
|
|
28438
28487
|
}
|
|
28439
28488
|
);
|
|
28440
28489
|
}
|
|
@@ -28443,7 +28492,6 @@ var init_daily_brief2 = __esm({
|
|
|
28443
28492
|
"src/tools/daily-brief.ts"() {
|
|
28444
28493
|
"use strict";
|
|
28445
28494
|
import_zod56 = require("zod");
|
|
28446
|
-
init_brief_answers();
|
|
28447
28495
|
init_tool_helpers();
|
|
28448
28496
|
init_ghl_client();
|
|
28449
28497
|
init_agency_profile();
|
|
@@ -28463,16 +28511,11 @@ var init_daily_brief2 = __esm({
|
|
|
28463
28511
|
|
|
28464
28512
|
// src/command-os/connectors/brief-panels.ts
|
|
28465
28513
|
function briefPanelDeps(input) {
|
|
28466
|
-
return {
|
|
28467
|
-
stripe: () => stripePanel(input),
|
|
28468
|
-
slack: () => slackPanel(input),
|
|
28469
|
-
calendar: () => calendarPanel(input),
|
|
28470
|
-
chat: (id, name) => chatPanel(id, name)(input)
|
|
28471
|
-
};
|
|
28514
|
+
return { stripe: () => stripePanel(input), slack: () => slackPanel(input), calendar: () => calendarPanel(input) };
|
|
28472
28515
|
}
|
|
28473
28516
|
function readTimes() {
|
|
28474
28517
|
try {
|
|
28475
|
-
return TimesSchema.parse(JSON.parse(
|
|
28518
|
+
return TimesSchema.parse(JSON.parse(fs21.readFileSync(timesPath(), "utf8")));
|
|
28476
28519
|
} catch {
|
|
28477
28520
|
return {};
|
|
28478
28521
|
}
|
|
@@ -28481,13 +28524,13 @@ function remember(id, readAt) {
|
|
|
28481
28524
|
const times = readTimes();
|
|
28482
28525
|
times[id] = readAt;
|
|
28483
28526
|
ensureAppDataDir();
|
|
28484
|
-
const tmp = `${timesPath()}.${(0,
|
|
28527
|
+
const tmp = `${timesPath()}.${(0, import_node_crypto6.randomUUID)()}.tmp`;
|
|
28485
28528
|
try {
|
|
28486
|
-
|
|
28487
|
-
|
|
28529
|
+
fs21.writeFileSync(tmp, JSON.stringify(times) + "\n", { mode: 384, flag: "wx" });
|
|
28530
|
+
fs21.renameSync(tmp, timesPath());
|
|
28488
28531
|
} finally {
|
|
28489
28532
|
try {
|
|
28490
|
-
|
|
28533
|
+
fs21.unlinkSync(tmp);
|
|
28491
28534
|
} catch {
|
|
28492
28535
|
}
|
|
28493
28536
|
}
|
|
@@ -28520,50 +28563,31 @@ function livePanel(id, name, read2) {
|
|
|
28520
28563
|
};
|
|
28521
28564
|
}
|
|
28522
28565
|
function money2(rows2) {
|
|
28523
|
-
const
|
|
28566
|
+
const totals2 = /* @__PURE__ */ new Map();
|
|
28524
28567
|
let unpriced = 0;
|
|
28525
28568
|
for (const row of rows2) {
|
|
28526
28569
|
if (row.amount === null || !row.currency) {
|
|
28527
28570
|
unpriced++;
|
|
28528
28571
|
continue;
|
|
28529
28572
|
}
|
|
28530
|
-
|
|
28573
|
+
totals2.set(row.currency, (totals2.get(row.currency) ?? 0) + row.amount);
|
|
28531
28574
|
}
|
|
28532
|
-
return { count: rows2.length, totals: [...
|
|
28533
|
-
}
|
|
28534
|
-
function chatPanel(id, name) {
|
|
28535
|
-
return async (_deps) => {
|
|
28536
|
-
try {
|
|
28537
|
-
const record = readBriefAnswers()[id];
|
|
28538
|
-
return {
|
|
28539
|
-
id,
|
|
28540
|
-
name,
|
|
28541
|
-
ok: Boolean(record),
|
|
28542
|
-
state: record ? "read" : "not-recorded",
|
|
28543
|
-
readAt: record?.readAt ?? null,
|
|
28544
|
-
why: record ? "Filled by your last chat brief" : "Nothing recorded yet. Ask Claude: run my morning brief",
|
|
28545
|
-
rows: record?.rows ?? [],
|
|
28546
|
-
needsYou: []
|
|
28547
|
-
};
|
|
28548
|
-
} catch {
|
|
28549
|
-
return failure(id, name, "The saved chat brief could not be read; ask Claude to record it again.");
|
|
28550
|
-
}
|
|
28551
|
-
};
|
|
28575
|
+
return { count: rows2.length, totals: [...totals2].map(([currency, amount]) => ({ currency, amount })), unpriced };
|
|
28552
28576
|
}
|
|
28553
28577
|
async function readBriefPanels(deps) {
|
|
28554
28578
|
return Promise.all(CONNECTOR_REGISTRY.flatMap((entry) => entry.panel ? [entry.panel(briefPanelDeps(deps))] : []));
|
|
28555
28579
|
}
|
|
28556
|
-
var
|
|
28580
|
+
var fs21, path19, import_node_crypto6, import_zod57, MoneySchema, TotalsSchema, SummarySchema, BriefPanelSchema, TimesSchema, timesPath, NotConnected, safeLink, amountText, stripePanel, slackPanel, calendarPanel;
|
|
28557
28581
|
var init_brief_panels = __esm({
|
|
28558
28582
|
"src/command-os/connectors/brief-panels.ts"() {
|
|
28559
28583
|
"use strict";
|
|
28560
28584
|
init_brief_deadline();
|
|
28561
|
-
|
|
28562
|
-
|
|
28563
|
-
|
|
28585
|
+
fs21 = __toESM(require("node:fs"));
|
|
28586
|
+
path19 = __toESM(require("node:path"));
|
|
28587
|
+
import_node_crypto6 = require("node:crypto");
|
|
28564
28588
|
import_zod57 = require("zod");
|
|
28565
28589
|
init_credentials_store();
|
|
28566
|
-
|
|
28590
|
+
init_brief_row();
|
|
28567
28591
|
init_registry();
|
|
28568
28592
|
init_store2();
|
|
28569
28593
|
init_handlers();
|
|
@@ -28573,6 +28597,17 @@ var init_brief_panels = __esm({
|
|
|
28573
28597
|
init_daily_brief2();
|
|
28574
28598
|
init_agency_profile();
|
|
28575
28599
|
MoneySchema = import_zod57.z.object({ count: import_zod57.z.number().int().nonnegative(), totals: import_zod57.z.array(import_zod57.z.object({ currency: import_zod57.z.string(), amount: import_zod57.z.number() })), unpriced: import_zod57.z.number().int().nonnegative() });
|
|
28600
|
+
TotalsSchema = import_zod57.z.array(import_zod57.z.object({ currency: import_zod57.z.string(), amount: import_zod57.z.number() }));
|
|
28601
|
+
SummarySchema = import_zod57.z.object({
|
|
28602
|
+
mrr: TotalsSchema,
|
|
28603
|
+
mrrUnpriced: import_zod57.z.number().int().nonnegative(),
|
|
28604
|
+
byProduct: import_zod57.z.array(import_zod57.z.object({ name: import_zod57.z.string(), subscribers: import_zod57.z.number().int().nonnegative(), currency: import_zod57.z.string(), monthly: import_zod57.z.number() })),
|
|
28605
|
+
received7: import_zod57.z.object({ count: import_zod57.z.number().int().nonnegative(), totals: TotalsSchema }),
|
|
28606
|
+
received30: import_zod57.z.object({ count: import_zod57.z.number().int().nonnegative(), totals: TotalsSchema }),
|
|
28607
|
+
recurring30: TotalsSchema,
|
|
28608
|
+
oneOff30: TotalsSchema,
|
|
28609
|
+
failed30: import_zod57.z.number().int().nonnegative()
|
|
28610
|
+
});
|
|
28576
28611
|
BriefPanelSchema = import_zod57.z.object({
|
|
28577
28612
|
id: import_zod57.z.string(),
|
|
28578
28613
|
name: import_zod57.z.string(),
|
|
@@ -28584,10 +28619,10 @@ var init_brief_panels = __esm({
|
|
|
28584
28619
|
why: import_zod57.z.string().optional(),
|
|
28585
28620
|
rows: import_zod57.z.array(BriefRowSchema),
|
|
28586
28621
|
needsYou: import_zod57.z.array(BriefRowSchema),
|
|
28587
|
-
money: import_zod57.z.object({ owed: MoneySchema, renewals: MoneySchema }).optional()
|
|
28622
|
+
money: import_zod57.z.object({ owed: MoneySchema, renewals: MoneySchema, summary: SummarySchema.optional() }).optional()
|
|
28588
28623
|
});
|
|
28589
28624
|
TimesSchema = import_zod57.z.record(import_zod57.z.string(), import_zod57.z.iso.datetime({ offset: true }));
|
|
28590
|
-
timesPath = () =>
|
|
28625
|
+
timesPath = () => path19.join(appDataDir(), "brief-read-times.json");
|
|
28591
28626
|
NotConnected = class extends Error {
|
|
28592
28627
|
constructor() {
|
|
28593
28628
|
super("not connected");
|
|
@@ -28602,7 +28637,15 @@ var init_brief_panels = __esm({
|
|
|
28602
28637
|
stripePanel = livePanel("stripe", "Stripe", async ({ now, signal }) => {
|
|
28603
28638
|
if (!listConnectors({ strict: true }).some((c) => c.system === "stripe")) throw new NotConnected();
|
|
28604
28639
|
return withKey("stripe", async (key) => {
|
|
28605
|
-
const
|
|
28640
|
+
const fetcher = stripeFetcher(key, signal);
|
|
28641
|
+
const [result, paid] = await Promise.all([readStripe(fetcher), readStripeMoney(fetcher, now).then(
|
|
28642
|
+
(value) => ({ value, why: "" }),
|
|
28643
|
+
(error) => {
|
|
28644
|
+
if (error instanceof Error && error.name === "TimeoutError" || signal?.aborted) throw error;
|
|
28645
|
+
const text2 = error instanceof Error ? error.message : "";
|
|
28646
|
+
return { value: null, why: /401|403|permission/i.test(text2) ? "Money figures need Read access to Charges and Products on your Stripe key; add them in Stripe and the figures appear on the next refresh." : /more records than this read can cover/.test(text2) ? "Money figures are not shown: this account has more payments or products than one read covers." : "Money figures could not be read this time; try Refresh." };
|
|
28647
|
+
}
|
|
28648
|
+
)]);
|
|
28606
28649
|
const names = new Map(result.customers.map((c) => [c.id, c.name || c.email || c.id]));
|
|
28607
28650
|
const owed = outstanding(result.invoices, now);
|
|
28608
28651
|
const renewals = renewalsWithStale(result.subs, 30, now);
|
|
@@ -28614,8 +28657,12 @@ var init_brief_panels = __esm({
|
|
|
28614
28657
|
return {
|
|
28615
28658
|
rows: [...owed.map(invoiceRow), ...renewals.due.map((r) => ({ text: `${names.get(r.customerId) ?? r.customerId}: ${r.cancelling ? "cancels" : "renews"} at ${amountText(r.amount, r.currency)}`, when: r.renewsAt }))],
|
|
28616
28659
|
needsYou: owed.filter((i) => i.daysOverdue !== null).map(invoiceRow),
|
|
28617
|
-
money: {
|
|
28618
|
-
|
|
28660
|
+
money: {
|
|
28661
|
+
owed: money2(owed),
|
|
28662
|
+
renewals: money2(renewals.due),
|
|
28663
|
+
...paid.value ? { summary: moneySummary(result.subs, result.invoices, paid.value.charges, paid.value.products, now) } : {}
|
|
28664
|
+
},
|
|
28665
|
+
...renewals.stale.length || paid.why ? { why: [paid.why, renewals.stale.length ? `${renewals.stale.length} subscriptions have a past period end; they are excluded from renewals due.` : ""].filter(Boolean).join(" ") } : {}
|
|
28619
28666
|
};
|
|
28620
28667
|
});
|
|
28621
28668
|
});
|
|
@@ -28716,7 +28763,7 @@ var init_client_capabilities = __esm({
|
|
|
28716
28763
|
{ key: "reputation", label: "Reputation and content", blurb: "Reviews, social posts and the blog." },
|
|
28717
28764
|
{ key: "health", label: "Health and handoff", blurb: "Proving it works, keeping it clean, and handing it over." }
|
|
28718
28765
|
];
|
|
28719
|
-
N = (label,
|
|
28766
|
+
N = (label, path41, params, key, nameField, version) => ({ label, path: path41, params, key, nameField, version });
|
|
28720
28767
|
AI2 = "2021-04-15";
|
|
28721
28768
|
CAPABILITIES = [
|
|
28722
28769
|
// ── Set up ──────────────────────────────────────────────────────────────
|
|
@@ -28977,7 +29024,7 @@ var require_package = __commonJS({
|
|
|
28977
29024
|
"package.json"(exports2, module2) {
|
|
28978
29025
|
module2.exports = {
|
|
28979
29026
|
name: "@elitedcs/ghl-mcp",
|
|
28980
|
-
version: "3.84.
|
|
29027
|
+
version: "3.84.7",
|
|
28981
29028
|
mcpName: "io.github.drjerryrelth/ghl-command",
|
|
28982
29029
|
description: "GoHighLevel MCP Server for Claude. 250 tools \u2014 full CRM, automation, marketing control, account-wide workflow audit, live funnel-capture verification, and the only programmatic GHL workflow builder, now multi-tenant across client accounts.",
|
|
28983
29030
|
main: "dist/index.js",
|
|
@@ -29076,17 +29123,17 @@ var require_package = __commonJS({
|
|
|
29076
29123
|
});
|
|
29077
29124
|
|
|
29078
29125
|
// src/package-version.ts
|
|
29079
|
-
var
|
|
29126
|
+
var fs22, path20, bundledPkg, pkg;
|
|
29080
29127
|
var init_package_version = __esm({
|
|
29081
29128
|
"src/package-version.ts"() {
|
|
29082
29129
|
"use strict";
|
|
29083
|
-
|
|
29084
|
-
|
|
29130
|
+
fs22 = __toESM(require("fs"));
|
|
29131
|
+
path20 = __toESM(require("path"));
|
|
29085
29132
|
bundledPkg = require_package();
|
|
29086
29133
|
pkg = (() => {
|
|
29087
29134
|
try {
|
|
29088
29135
|
const onDisk = JSON.parse(
|
|
29089
|
-
|
|
29136
|
+
fs22.readFileSync(path20.resolve(__dirname, "..", "package.json"), "utf8")
|
|
29090
29137
|
);
|
|
29091
29138
|
if (typeof onDisk.version === "string" && onDisk.version.length > 0) {
|
|
29092
29139
|
return { version: onDisk.version };
|
|
@@ -29356,6 +29403,7 @@ main.brief{max-width:64rem;margin:auto;padding:0 1.2rem 4rem;color:var(--ink);li
|
|
|
29356
29403
|
.brief .stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(11rem,1fr));gap:.7rem;margin-top:1.4rem}
|
|
29357
29404
|
.brief .stat{background:var(--surface);border:1px solid var(--line);border-radius:var(--radius);padding:.9rem 1rem}
|
|
29358
29405
|
.brief .stat .big{display:block;font-family:var(--font-display);font-weight:600;font-size:1.5rem;line-height:1.15;color:var(--heading);font-variant-numeric:tabular-nums}
|
|
29406
|
+
.brief .stat.warn .big{color:var(--warn)}.brief .money-stats{margin:0 0 1rem}
|
|
29359
29407
|
.brief .stat .lbl{display:block;font-size:var(--fs-label);letter-spacing:.12em;text-transform:uppercase;color:var(--ink-3);margin-top:.3rem}
|
|
29360
29408
|
.brief .stats-meta{margin:.8rem 0 0;font-size:var(--fs-sm);color:var(--ink-3)}
|
|
29361
29409
|
.brief blockquote{margin:1.2rem 0 0;padding:.2rem 0 .2rem 1rem;border-left:3px solid var(--gold);font-family:var(--font-display);font-size:1.25rem;line-height:1.4;color:var(--heading)}
|
|
@@ -29450,6 +29498,8 @@ const panelDate=p=>{
|
|
|
29450
29498
|
return line;
|
|
29451
29499
|
};
|
|
29452
29500
|
const panelTone=p=>p.state==="read"?"ok":p.state==="not-connected"?"wait":p.state==="not-recorded"?"draft":"warn";
|
|
29501
|
+
const cash=(t)=>{try{return new Intl.NumberFormat(undefined,{style:"currency",currency:t.currency,maximumFractionDigits:Number.isInteger(t.amount)?0:2}).format(t.amount)}catch{return t.currency+" "+t.amount.toFixed(2)}};
|
|
29502
|
+
const cashAll=ts=>ts.length?ts.map(cash).join(" + "):cash({currency:"USD",amount:0});
|
|
29453
29503
|
const amount=m=>m.totals.length?m.totals.map(t=>t.currency+" "+t.amount.toFixed(2)).join(" + "):m.count?"Total not stated":"No amount due";
|
|
29454
29504
|
const plural=(n,one,many)=>n+" "+(n===1?one:many);
|
|
29455
29505
|
/* The work queue's titles carry the client's name ("Fix 2 things the verify
|
|
@@ -29523,8 +29573,10 @@ function draw(){
|
|
|
29523
29573
|
tile((b.readFrom?.accounts||0)+" of "+data.accounts.total,"accounts read");
|
|
29524
29574
|
tile(needs.length,"needs you today");
|
|
29525
29575
|
const stripe=data.panels.find(p=>p.id==="stripe");const money=stripe?.ok?stripe.money:null;
|
|
29526
|
-
|
|
29527
|
-
|
|
29576
|
+
const sum=money?.summary;
|
|
29577
|
+
if(sum){tile(cashAll(sum.mrr),"monthly recurring revenue");tile(cashAll(sum.received30.totals),"received before Stripe fees, last 30 days")}
|
|
29578
|
+
else if(money){tile(amount(money.owed),"owed on "+plural(money.owed.count,"open invoice","open invoices"));tile(money.renewals.count,"renewals within 30 days")}
|
|
29579
|
+
else{tile("Not read","monthly recurring revenue");tile("Not read","received before Stripe fees, last 30 days")}
|
|
29528
29580
|
tile(data.accounts.unread,"accounts could not be read");
|
|
29529
29581
|
tiles.appendChild(el("p",readLine(),"stats-meta"));
|
|
29530
29582
|
const tb=el("div","","tiles-bar");const total=data.accounts.total||0,got=b.readFrom?.accounts||0;tb.appendChild(el("span",got+" of "+total+" accounts read","lbl"));const bar=el("div","","bar");const fill=el("i");fill.style.width=(total?Math.round(100*got/total):0)+"%";bar.appendChild(fill);tb.appendChild(bar);tiles.appendChild(tb);
|
|
@@ -29565,10 +29617,20 @@ function draw(){
|
|
|
29565
29617
|
list(card(lc,"By source"),(b.leadsBySource||[]).map(r=>({text:r.client+": "+r.line})),"No source breakdown available.");
|
|
29566
29618
|
/* One panel per connected system */
|
|
29567
29619
|
data.panels.forEach(p=>{
|
|
29568
|
-
const
|
|
29620
|
+
const isMoney=p.id==="stripe";
|
|
29621
|
+
const s=section(root,"panel-"+p.id,isMoney?"From Stripe":"Connected system",isMoney?"Money":p.name,"",panelDate(p));
|
|
29569
29622
|
const head=el("div","","panel-head");head.appendChild(el("p","Results from "+p.name+" for this brief.","intro"));
|
|
29570
29623
|
const button=el("button","Refresh "+p.name,"refresh");button.type="button";button.dataset.refresh=p.id;button.onclick=()=>refreshPanel(p.id,button);head.appendChild(button);s.appendChild(head);
|
|
29571
29624
|
const c=card(s,"");
|
|
29625
|
+
const sm=p.id==="stripe"&&p.ok?p.money?.summary:null;
|
|
29626
|
+
if(sm){const g=el("div","","stats money-stats");const st=(v,l,w)=>{const b=el("div","","stat"+(w?" warn":""));b.appendChild(el("strong",String(v),"big"));b.appendChild(el("span",l,"lbl"));g.appendChild(b)};
|
|
29627
|
+
st(cashAll(sm.mrr),"monthly recurring revenue");st(cashAll(sm.received7.totals),"received before Stripe fees, last 7 days \xB7 "+plural(sm.received7.count,"payment","payments"));
|
|
29628
|
+
st(cashAll(sm.received30.totals),"received before Stripe fees, last 30 days \xB7 "+plural(sm.received30.count,"payment","payments"));st(sm.failed30,"failed payment attempts, last 30 days (Stripe retries count each time)",sm.failed30>0);c.appendChild(g);
|
|
29629
|
+
if(sm.byProduct.length){const t=el("table","","rows");const hr=el("tr");["Product","Subscribers","Monthly"].forEach(h=>hr.appendChild(el("th",h)));t.appendChild(hr);
|
|
29630
|
+
sm.byProduct.forEach(r=>{const tr=el("tr");tr.appendChild(el("td",r.name));tr.appendChild(el("td",String(r.subscribers),"num"));tr.appendChild(el("td",cash({currency:r.currency,amount:r.monthly}),"num"));t.appendChild(tr)});c.appendChild(t)}
|
|
29631
|
+
const split=el("table");const srow=(k,v)=>{const tr=el("tr");tr.appendChild(el("th",k));tr.appendChild(el("td",v,"num"));split.appendChild(tr)};
|
|
29632
|
+
srow("Subscription invoices paid, last 30 days",cashAll(sm.recurring30));srow("Everything else received, last 30 days",cashAll(sm.oneOff30));c.appendChild(split);
|
|
29633
|
+
c.appendChild(el("p","MRR is list price per month for active and past-due subscriptions. Everything else received is money in minus subscription invoices paid, never below zero."+(sm.mrrUnpriced?" "+plural(sm.mrrUnpriced,"line has no fixed price and is","lines have no fixed price and are")+" not counted.":""),"intro"))}
|
|
29572
29634
|
if(p.id==="stripe"&&p.ok&&p.money){const t=el("table");const row=(k,v)=>{const tr=el("tr");tr.appendChild(el("th",k));tr.appendChild(el("td",v,"num"));t.appendChild(tr)};
|
|
29573
29635
|
row(plural(p.money.owed.count,"open invoice","open invoices"),amount(p.money.owed));row(plural(p.money.renewals.count,"renewal within 30 days","renewals within 30 days"),amount(p.money.renewals));
|
|
29574
29636
|
if(p.money.owed.unpriced||p.money.renewals.unpriced)row("Amounts not stated",String(p.money.owed.unpriced+p.money.renewals.unpriced));c.appendChild(t)}
|
|
@@ -29576,6 +29638,7 @@ function draw(){
|
|
|
29576
29638
|
if(p.id==="stripe")rowsTable(c,marked.map(splitRow),["Customer","What","When"],p.ok?"No items returned by this source.":"");
|
|
29577
29639
|
else list(c,marked,p.ok?"No items returned by this source.":"");
|
|
29578
29640
|
});
|
|
29641
|
+
const mail=section(root,"mail","Mail, files and calendar","In your Claude brief","");mail.appendChild(el("p","Your Claude reads your mail, files and calendar in its own morning brief. Ask it: \u201CRun my morning brief.\u201D","intro"));
|
|
29579
29642
|
highlight();
|
|
29580
29643
|
}
|
|
29581
29644
|
/* The active pill follows the section in view. */
|
|
@@ -29679,15 +29742,15 @@ async function loadBriefDashboard(registry2, now = /* @__PURE__ */ new Date(), s
|
|
|
29679
29742
|
}
|
|
29680
29743
|
async function secretsKnownToThisProcess(registry2, extras = []) {
|
|
29681
29744
|
const secrets = /* @__PURE__ */ new Set();
|
|
29682
|
-
const
|
|
29745
|
+
const add2 = (key) => {
|
|
29683
29746
|
if (typeof key !== "string" || !key.trim()) throw new Error("A saved credential could not be read.");
|
|
29684
29747
|
secrets.add(key);
|
|
29685
29748
|
};
|
|
29686
29749
|
const addOptional = (key) => {
|
|
29687
29750
|
if (typeof key === "string" && !key.trim()) return;
|
|
29688
|
-
|
|
29751
|
+
add2(key);
|
|
29689
29752
|
};
|
|
29690
|
-
extras.forEach(
|
|
29753
|
+
extras.forEach(add2);
|
|
29691
29754
|
const credentialFields = [
|
|
29692
29755
|
["license_key", "GHL_LICENSE_KEY"],
|
|
29693
29756
|
["ghl_api_key", "GHL_API_KEY"],
|
|
@@ -29708,14 +29771,14 @@ async function secretsKnownToThisProcess(registry2, extras = []) {
|
|
|
29708
29771
|
}
|
|
29709
29772
|
for (const { system } of listConnectors({ strict: true })) {
|
|
29710
29773
|
await withKey(system, async (key) => {
|
|
29711
|
-
|
|
29774
|
+
add2(key);
|
|
29712
29775
|
});
|
|
29713
29776
|
}
|
|
29714
29777
|
if (calendarConnectionStatus().connected) {
|
|
29715
29778
|
await withCalendarSecret(async (secret) => {
|
|
29716
|
-
|
|
29717
|
-
|
|
29718
|
-
|
|
29779
|
+
add2(secret.clientId);
|
|
29780
|
+
add2(secret.clientSecret);
|
|
29781
|
+
add2(secret.refreshToken);
|
|
29719
29782
|
});
|
|
29720
29783
|
}
|
|
29721
29784
|
if (registry2) {
|
|
@@ -29727,7 +29790,7 @@ async function secretsKnownToThisProcess(registry2, extras = []) {
|
|
|
29727
29790
|
for (const { locationId: locationId2 } of locations) {
|
|
29728
29791
|
const token2 = registry2.getToken(locationId2);
|
|
29729
29792
|
if (!token2) throw new Error("Account credentials could not be read.");
|
|
29730
|
-
|
|
29793
|
+
add2(token2.apiKey);
|
|
29731
29794
|
}
|
|
29732
29795
|
const agency = registry2.getAgencyKey();
|
|
29733
29796
|
if (agency !== void 0) addOptional(agency);
|
|
@@ -31311,7 +31374,7 @@ function scrubForClient(text2) {
|
|
|
31311
31374
|
}
|
|
31312
31375
|
function clientNextSteps(items) {
|
|
31313
31376
|
const steps = [];
|
|
31314
|
-
const
|
|
31377
|
+
const add2 = (s) => {
|
|
31315
31378
|
if (s && !steps.includes(s)) steps.push(s);
|
|
31316
31379
|
};
|
|
31317
31380
|
for (const it of items) {
|
|
@@ -31319,12 +31382,12 @@ function clientNextSteps(items) {
|
|
|
31319
31382
|
if (it.state === "waiting") {
|
|
31320
31383
|
const who = scrubForClient(it.resolvedBy ?? "");
|
|
31321
31384
|
if (who) {
|
|
31322
|
-
|
|
31385
|
+
add2(`${it.title}: ${who}${it.roughly ? ` \u2014 roughly ${scrubForClient(it.roughly)}` : ""}`);
|
|
31323
31386
|
continue;
|
|
31324
31387
|
}
|
|
31325
31388
|
}
|
|
31326
31389
|
const rule5 = NEXT_STEP_RULES.find((r) => r.re.test(it.sentence));
|
|
31327
|
-
|
|
31390
|
+
add2(rule5 ? rule5.step : `Confirm "${it.title}" with your team once the item above has been sorted.`);
|
|
31328
31391
|
}
|
|
31329
31392
|
return steps;
|
|
31330
31393
|
}
|
|
@@ -31659,11 +31722,11 @@ var init_client_status = __esm({
|
|
|
31659
31722
|
|
|
31660
31723
|
// src/command-os/client-status-placement.ts
|
|
31661
31724
|
function statusStorePath(base = appDataDir()) {
|
|
31662
|
-
return
|
|
31725
|
+
return path21.join(base, "client-status.json");
|
|
31663
31726
|
}
|
|
31664
31727
|
function readStatusStore(base) {
|
|
31665
31728
|
try {
|
|
31666
|
-
const raw = JSON.parse(
|
|
31729
|
+
const raw = JSON.parse(fs23.readFileSync(statusStorePath(base), "utf8"));
|
|
31667
31730
|
if (raw && raw.v === 1 && raw.clients) return raw;
|
|
31668
31731
|
} catch {
|
|
31669
31732
|
}
|
|
@@ -31671,8 +31734,8 @@ function readStatusStore(base) {
|
|
|
31671
31734
|
}
|
|
31672
31735
|
function writeStatusStore(store, base) {
|
|
31673
31736
|
const file2 = statusStorePath(base);
|
|
31674
|
-
|
|
31675
|
-
|
|
31737
|
+
fs23.mkdirSync(path21.dirname(file2), { recursive: true, mode: 448 });
|
|
31738
|
+
fs23.writeFileSync(file2, JSON.stringify(store, null, 2), { mode: 384 });
|
|
31676
31739
|
}
|
|
31677
31740
|
function readStatusRecord(locationId2, base) {
|
|
31678
31741
|
return readStatusStore(base).clients[locationId2] ?? { locationId: locationId2 };
|
|
@@ -31704,9 +31767,9 @@ function describePlacement(placement) {
|
|
|
31704
31767
|
return `A real page inside the client's own GoHighLevel account, at /${placement.slug ?? STATUS_SLUG}${placement.publicUrl ? ` \u2014 ${placement.publicUrl}` : ""}. Every re-check replaces that one page; it never adds a second copy.`;
|
|
31705
31768
|
}
|
|
31706
31769
|
if (placement.kind === "agency-site") {
|
|
31707
|
-
return `A file on your own site: ${
|
|
31770
|
+
return `A file on your own site: ${path21.join(placement.directory, placement.fileName ?? "status.html")}${placement.publicUrl ? `, which you publish at ${placement.publicUrl}` : ""}. The same file is rewritten every time, so the address never changes.`;
|
|
31708
31771
|
}
|
|
31709
|
-
return `A page written to ${
|
|
31772
|
+
return `A page written to ${path21.join(placement.directory, placement.fileName ?? "status.html")} for you to publish as an Artifact from your own Claude${placement.publicUrl ? `, currently at ${placement.publicUrl}` : ""}.`;
|
|
31710
31773
|
}
|
|
31711
31774
|
function placementReadiness(placement) {
|
|
31712
31775
|
if (!placement) return ["Choose where this client's page should live: their own GoHighLevel account, your own site, or an Artifact from your own Claude."];
|
|
@@ -31765,12 +31828,12 @@ function artifactPublishSteps(filePath, existingUrl) {
|
|
|
31765
31828
|
"Send the client the link once. From then on they just revisit it."
|
|
31766
31829
|
];
|
|
31767
31830
|
}
|
|
31768
|
-
var
|
|
31831
|
+
var fs23, path21, STATUS_SLUG, OUR_HOSTS;
|
|
31769
31832
|
var init_client_status_placement = __esm({
|
|
31770
31833
|
"src/command-os/client-status-placement.ts"() {
|
|
31771
31834
|
"use strict";
|
|
31772
|
-
|
|
31773
|
-
|
|
31835
|
+
fs23 = __toESM(require("node:fs"));
|
|
31836
|
+
path21 = __toESM(require("node:path"));
|
|
31774
31837
|
init_credentials_store();
|
|
31775
31838
|
init_client_status();
|
|
31776
31839
|
STATUS_SLUG = "status";
|
|
@@ -31790,7 +31853,7 @@ var init_client_status_placement = __esm({
|
|
|
31790
31853
|
// src/build-contract.ts
|
|
31791
31854
|
function contentHash(value) {
|
|
31792
31855
|
const canonical2 = (v) => Array.isArray(v) ? v.map(canonical2) : v && typeof v === "object" ? Object.fromEntries(Object.keys(v).sort().filter((k) => v[k] !== void 0).map((k) => [k, canonical2(v[k])])) : v;
|
|
31793
|
-
return (0,
|
|
31856
|
+
return (0, import_node_crypto7.createHash)("sha256").update(JSON.stringify(canonical2(value)) ?? "null").digest("hex");
|
|
31794
31857
|
}
|
|
31795
31858
|
function assertBuildApproval(approval, inputHash) {
|
|
31796
31859
|
if (!approval?.at) throw new Error("Approve building before running Build.");
|
|
@@ -31965,11 +32028,11 @@ function executablePlanLines(saved) {
|
|
|
31965
32028
|
for (const w of preview?.workflows ?? []) for (const m of [...w.manual ?? [], ...w.needsContent ?? []]) lines.push(`Unresolved: ${w.name || names.get(w.ref) || "Unnamed workflow"}. ${m.reason}`);
|
|
31966
32029
|
return lines.map(sentence);
|
|
31967
32030
|
}
|
|
31968
|
-
var
|
|
32031
|
+
var import_node_crypto7, import_node_fs3, import_node_path3, INTAKE_CHANGED;
|
|
31969
32032
|
var init_build_contract = __esm({
|
|
31970
32033
|
"src/build-contract.ts"() {
|
|
31971
32034
|
"use strict";
|
|
31972
|
-
|
|
32035
|
+
import_node_crypto7 = require("node:crypto");
|
|
31973
32036
|
import_node_fs3 = require("node:fs");
|
|
31974
32037
|
import_node_path3 = require("node:path");
|
|
31975
32038
|
init_credentials_store();
|
|
@@ -32229,30 +32292,30 @@ var init_client_status_page = __esm({
|
|
|
32229
32292
|
|
|
32230
32293
|
// src/command-os/client-status-publish.ts
|
|
32231
32294
|
function handoffSnapshotDir(base = appDataDir()) {
|
|
32232
|
-
return
|
|
32295
|
+
return path22.join(base, "handoffs");
|
|
32233
32296
|
}
|
|
32234
32297
|
function handoffSnapshotPath(locationId2, base) {
|
|
32235
|
-
return
|
|
32298
|
+
return path22.join(handoffSnapshotDir(base), `${locationId2}-handoff.json`);
|
|
32236
32299
|
}
|
|
32237
32300
|
function saveHandoffSnapshot(saved, base) {
|
|
32238
32301
|
const file2 = handoffSnapshotPath(saved.locationId, base);
|
|
32239
|
-
|
|
32240
|
-
|
|
32302
|
+
fs24.mkdirSync(path22.dirname(file2), { recursive: true, mode: 448 });
|
|
32303
|
+
fs24.writeFileSync(file2, JSON.stringify(saved, null, 2), { mode: 384 });
|
|
32241
32304
|
return file2;
|
|
32242
32305
|
}
|
|
32243
32306
|
function readHandoffSnapshot(locationId2, base) {
|
|
32244
32307
|
try {
|
|
32245
|
-
return JSON.parse(
|
|
32308
|
+
return JSON.parse(fs24.readFileSync(handoffSnapshotPath(locationId2, base), "utf8"));
|
|
32246
32309
|
} catch {
|
|
32247
32310
|
return null;
|
|
32248
32311
|
}
|
|
32249
32312
|
}
|
|
32250
32313
|
function localCopyPath(locationId2, base = appDataDir()) {
|
|
32251
|
-
return
|
|
32314
|
+
return path22.join(base, "client-status", `${locationId2}-status.html`);
|
|
32252
32315
|
}
|
|
32253
32316
|
function readVerifyOutcome(locationId2, base = appDataDir()) {
|
|
32254
32317
|
try {
|
|
32255
|
-
const st = JSON.parse(
|
|
32318
|
+
const st = JSON.parse(fs24.readFileSync(path22.join(base, "cockpit-state.json"), "utf8"));
|
|
32256
32319
|
const o = st.outcomes?.[`${locationId2}:4`];
|
|
32257
32320
|
if (!o) return null;
|
|
32258
32321
|
const src = o.plain ?? { summary: o.summary, issues: o.issues };
|
|
@@ -32262,9 +32325,9 @@ function readVerifyOutcome(locationId2, base = appDataDir()) {
|
|
|
32262
32325
|
}
|
|
32263
32326
|
}
|
|
32264
32327
|
function writeFileFor(dir, fileName, html2) {
|
|
32265
|
-
|
|
32266
|
-
const file2 =
|
|
32267
|
-
|
|
32328
|
+
fs24.mkdirSync(dir, { recursive: true });
|
|
32329
|
+
const file2 = path22.join(dir, fileName);
|
|
32330
|
+
fs24.writeFileSync(file2, html2);
|
|
32268
32331
|
return file2;
|
|
32269
32332
|
}
|
|
32270
32333
|
async function refreshClientStatus(opts) {
|
|
@@ -32300,7 +32363,7 @@ async function refreshClientStatus(opts) {
|
|
|
32300
32363
|
record.preparedSentences = input.items.map((i) => i.sentence);
|
|
32301
32364
|
if (!opts.dryRun) saveStatusRecord({ ...record, locationId: opts.locationId }, opts.base);
|
|
32302
32365
|
const localPath = localCopyPath(opts.locationId, opts.base);
|
|
32303
|
-
if (!opts.dryRun) writeFileFor(
|
|
32366
|
+
if (!opts.dryRun) writeFileFor(path22.dirname(localPath), path22.basename(localPath), html2);
|
|
32304
32367
|
const placement = record.placement;
|
|
32305
32368
|
const missing = placementReadiness(placement);
|
|
32306
32369
|
if (!placement || missing.length) {
|
|
@@ -32347,7 +32410,7 @@ async function publishTo(placement, input, html2, opts) {
|
|
|
32347
32410
|
if (placement.kind === "agency-site") {
|
|
32348
32411
|
assertNotOurHost(placement.publicUrl, "The address you chose for this page");
|
|
32349
32412
|
const fileName = placement.fileName ?? "status.html";
|
|
32350
|
-
const destinationPath = opts.dryRun ?
|
|
32413
|
+
const destinationPath = opts.dryRun ? path22.join(placement.directory, fileName) : writeFileFor(placement.directory, fileName, html2);
|
|
32351
32414
|
return {
|
|
32352
32415
|
...placement.publicUrl && { url: placement.publicUrl },
|
|
32353
32416
|
destinationPath,
|
|
@@ -32361,7 +32424,7 @@ async function publishTo(placement, input, html2, opts) {
|
|
|
32361
32424
|
const safety = artifactSafety(html2);
|
|
32362
32425
|
if (!safety.ok) throw new Error(safety.problems.join(" "));
|
|
32363
32426
|
const fileName = placement.fileName ?? "status.html";
|
|
32364
|
-
const destinationPath = opts.dryRun ?
|
|
32427
|
+
const destinationPath = opts.dryRun ? path22.join(placement.directory, fileName) : writeFileFor(placement.directory, fileName, html2);
|
|
32365
32428
|
assertNotOurHost(placement.publicUrl, "The Artifact link saved for this client");
|
|
32366
32429
|
return {
|
|
32367
32430
|
...placement.publicUrl && { url: placement.publicUrl },
|
|
@@ -32402,13 +32465,13 @@ async function refreshAfterVerify(locationId2, accountName, emit2, base, approve
|
|
|
32402
32465
|
emit2(`The client's status page was not updated: ${e instanceof Error ? e.message : String(e)}`);
|
|
32403
32466
|
}
|
|
32404
32467
|
}
|
|
32405
|
-
var
|
|
32468
|
+
var fs24, path22;
|
|
32406
32469
|
var init_client_status_publish = __esm({
|
|
32407
32470
|
"src/command-os/client-status-publish.ts"() {
|
|
32408
32471
|
"use strict";
|
|
32409
32472
|
init_publication_proof();
|
|
32410
|
-
|
|
32411
|
-
|
|
32473
|
+
fs24 = __toESM(require("node:fs"));
|
|
32474
|
+
path22 = __toESM(require("node:path"));
|
|
32412
32475
|
init_credentials_store();
|
|
32413
32476
|
init_branding();
|
|
32414
32477
|
init_client_status();
|
|
@@ -32487,11 +32550,11 @@ var init_handoff_snapshot = __esm({
|
|
|
32487
32550
|
// src/build-obligations.ts
|
|
32488
32551
|
function fileFor(id) {
|
|
32489
32552
|
if (!/^[a-zA-Z0-9_-]+$/.test(id)) throw new Error("Choose a registered client.");
|
|
32490
|
-
return
|
|
32553
|
+
return path23.join(appDataDir(), "build-obligations", id + ".json");
|
|
32491
32554
|
}
|
|
32492
32555
|
function readBuildObligations(id) {
|
|
32493
32556
|
try {
|
|
32494
|
-
const data = JSON.parse(
|
|
32557
|
+
const data = JSON.parse(fs25.readFileSync(fileFor(id), "utf8"));
|
|
32495
32558
|
return Array.isArray(data) ? data : [];
|
|
32496
32559
|
} catch (e) {
|
|
32497
32560
|
if (e.code === "ENOENT") return [];
|
|
@@ -32502,9 +32565,9 @@ function saveBuildObligations(id, items) {
|
|
|
32502
32565
|
const merged = new Map(readBuildObligations(id).map((m) => [`${m.type}:${m.ref}`, m]));
|
|
32503
32566
|
for (const item of items) merged.set(`${item.type}:${item.ref}`, item);
|
|
32504
32567
|
const file2 = fileFor(id);
|
|
32505
|
-
|
|
32506
|
-
|
|
32507
|
-
|
|
32568
|
+
fs25.mkdirSync(path23.dirname(file2), { recursive: true });
|
|
32569
|
+
fs25.writeFileSync(file2 + ".tmp", JSON.stringify([...merged.values()], null, 2), { mode: 384 });
|
|
32570
|
+
fs25.renameSync(file2 + ".tmp", file2);
|
|
32508
32571
|
}
|
|
32509
32572
|
function planObjectRefs(plan) {
|
|
32510
32573
|
const refs = /* @__PURE__ */ new Set();
|
|
@@ -32529,16 +32592,16 @@ function reconcileBuildObligations(id, checks, currentPlan) {
|
|
|
32529
32592
|
item.type === "workflow-manual" && Array.isArray(workflows) && workflows.some((workflow) => item.ref.startsWith(`${workflow.name}:`) && /^\d+$/.test(item.ref.slice(workflow.name.length + 1)));
|
|
32530
32593
|
const remaining = readBuildObligations(id).filter((item) => stillPlanned(item) && !checks.some((c) => c.ref === item.ref && c.type === item.type && c.ok === true && typeof c.evidence === "string" && c.evidence.trim()));
|
|
32531
32594
|
const file2 = fileFor(id);
|
|
32532
|
-
|
|
32533
|
-
|
|
32534
|
-
|
|
32595
|
+
fs25.mkdirSync(path23.dirname(file2), { recursive: true });
|
|
32596
|
+
fs25.writeFileSync(file2 + ".tmp", JSON.stringify(remaining, null, 2), { mode: 384 });
|
|
32597
|
+
fs25.renameSync(file2 + ".tmp", file2);
|
|
32535
32598
|
}
|
|
32536
|
-
var
|
|
32599
|
+
var fs25, path23;
|
|
32537
32600
|
var init_build_obligations = __esm({
|
|
32538
32601
|
"src/build-obligations.ts"() {
|
|
32539
32602
|
"use strict";
|
|
32540
|
-
|
|
32541
|
-
|
|
32603
|
+
fs25 = __toESM(require("node:fs"));
|
|
32604
|
+
path23 = __toESM(require("node:path"));
|
|
32542
32605
|
init_credentials_store();
|
|
32543
32606
|
}
|
|
32544
32607
|
});
|
|
@@ -32548,7 +32611,7 @@ function engineRepair(version) {
|
|
|
32548
32611
|
return !version ? CLAUDE_REPAIR_MESSAGES[0] : !meetsFloor(version) ? CLAUDE_REPAIR_MESSAGES[1] : CLAUDE_REPAIR_MESSAGES[2];
|
|
32549
32612
|
}
|
|
32550
32613
|
function keyFingerprint(key) {
|
|
32551
|
-
return (0,
|
|
32614
|
+
return (0, import_node_crypto8.createHash)("sha256").update(key).digest("hex");
|
|
32552
32615
|
}
|
|
32553
32616
|
async function clientPreflight(registry2, locationId2) {
|
|
32554
32617
|
const lines = [];
|
|
@@ -32615,7 +32678,7 @@ async function agencyConnectionReady(registry2, companyId) {
|
|
|
32615
32678
|
return false;
|
|
32616
32679
|
}
|
|
32617
32680
|
}
|
|
32618
|
-
var
|
|
32681
|
+
var import_node_crypto8, CLAUDE_REPAIR_MESSAGES;
|
|
32619
32682
|
var init_client_preflight = __esm({
|
|
32620
32683
|
"src/client-preflight.ts"() {
|
|
32621
32684
|
"use strict";
|
|
@@ -32625,7 +32688,7 @@ var init_client_preflight = __esm({
|
|
|
32625
32688
|
init_stage_runner();
|
|
32626
32689
|
init_tool_filter();
|
|
32627
32690
|
init_credentials_store();
|
|
32628
|
-
|
|
32691
|
+
import_node_crypto8 = require("node:crypto");
|
|
32629
32692
|
CLAUDE_REPAIR_MESSAGES = [
|
|
32630
32693
|
"Install Claude Code from Your agency before reading this source.",
|
|
32631
32694
|
"Update Claude Code to version 2.1.0 or newer from Your agency.",
|
|
@@ -32732,7 +32795,7 @@ function startDesignRun(request, opts = {}) {
|
|
|
32732
32795
|
const checked = validateRunRequest(request);
|
|
32733
32796
|
if (!checked.ok) throw new Error(checked.error);
|
|
32734
32797
|
request = checked.value;
|
|
32735
|
-
const id = (0,
|
|
32798
|
+
const id = (0, import_node_crypto9.randomUUID)();
|
|
32736
32799
|
const outDir = (0, import_node_path5.join)(designRunsDir(), id);
|
|
32737
32800
|
(0, import_node_fs5.mkdirSync)(outDir, { recursive: true });
|
|
32738
32801
|
const job = { id, status: "running", startedAt: (/* @__PURE__ */ new Date()).toISOString(), outDir, request, tail: [] };
|
|
@@ -32799,14 +32862,14 @@ function cancelDesignRun(id) {
|
|
|
32799
32862
|
}
|
|
32800
32863
|
return true;
|
|
32801
32864
|
}
|
|
32802
|
-
var import_node_child_process3, import_node_fs5, import_node_path5,
|
|
32865
|
+
var import_node_child_process3, import_node_fs5, import_node_path5, import_node_crypto9, SLUG2, MODEL, JOINED_FLAG, jobs, children;
|
|
32803
32866
|
var init_run_jobs = __esm({
|
|
32804
32867
|
"src/command-os/design/run-jobs.ts"() {
|
|
32805
32868
|
"use strict";
|
|
32806
32869
|
import_node_child_process3 = require("node:child_process");
|
|
32807
32870
|
import_node_fs5 = require("node:fs");
|
|
32808
32871
|
import_node_path5 = require("node:path");
|
|
32809
|
-
|
|
32872
|
+
import_node_crypto9 = require("node:crypto");
|
|
32810
32873
|
init_credentials_store();
|
|
32811
32874
|
init_schema();
|
|
32812
32875
|
init_brief_validation();
|
|
@@ -32834,20 +32897,20 @@ var init_run_expectations = __esm({
|
|
|
32834
32897
|
|
|
32835
32898
|
// src/run-cost.ts
|
|
32836
32899
|
function runCostPath() {
|
|
32837
|
-
return
|
|
32900
|
+
return path24.join(appDataDir(), "command-os-run-cost.jsonl");
|
|
32838
32901
|
}
|
|
32839
32902
|
function recordRunCost(rec, file2 = runCostPath()) {
|
|
32840
|
-
|
|
32841
|
-
|
|
32903
|
+
fs26.mkdirSync(path24.dirname(file2), { recursive: true });
|
|
32904
|
+
fs26.appendFileSync(file2, `${JSON.stringify(rec)}
|
|
32842
32905
|
`, { mode: 384 });
|
|
32843
32906
|
try {
|
|
32844
|
-
|
|
32907
|
+
fs26.chmodSync(file2, 384);
|
|
32845
32908
|
} catch {
|
|
32846
32909
|
}
|
|
32847
32910
|
}
|
|
32848
32911
|
function readRunCosts(file2 = runCostPath()) {
|
|
32849
32912
|
try {
|
|
32850
|
-
return
|
|
32913
|
+
return fs26.readFileSync(file2, "utf8").split("\n").filter((l) => l.trim()).map((l) => {
|
|
32851
32914
|
try {
|
|
32852
32915
|
return JSON.parse(l);
|
|
32853
32916
|
} catch {
|
|
@@ -32878,12 +32941,12 @@ function costLines(o) {
|
|
|
32878
32941
|
if (o.engineCheckRan && o.engineCheckCostUsd === void 0) out.push("Claude readiness check: cost not reported.");
|
|
32879
32942
|
return out;
|
|
32880
32943
|
}
|
|
32881
|
-
var
|
|
32944
|
+
var fs26, path24;
|
|
32882
32945
|
var init_run_cost = __esm({
|
|
32883
32946
|
"src/run-cost.ts"() {
|
|
32884
32947
|
"use strict";
|
|
32885
|
-
|
|
32886
|
-
|
|
32948
|
+
fs26 = __toESM(require("fs"));
|
|
32949
|
+
path24 = __toESM(require("path"));
|
|
32887
32950
|
init_credentials_store();
|
|
32888
32951
|
}
|
|
32889
32952
|
});
|
|
@@ -33685,9 +33748,9 @@ function presetForBusinessType(type) {
|
|
|
33685
33748
|
return "generic";
|
|
33686
33749
|
}
|
|
33687
33750
|
}
|
|
33688
|
-
function setPath(target,
|
|
33751
|
+
function setPath(target, path41, value) {
|
|
33689
33752
|
if (value === void 0) return;
|
|
33690
|
-
const parts =
|
|
33753
|
+
const parts = path41.split(".");
|
|
33691
33754
|
let node = target;
|
|
33692
33755
|
for (let i = 0; i < parts.length - 1; i++) {
|
|
33693
33756
|
const k = parts[i];
|
|
@@ -34486,17 +34549,17 @@ function planFromPreset(input) {
|
|
|
34486
34549
|
}
|
|
34487
34550
|
function presetsRoot() {
|
|
34488
34551
|
const candidates = [
|
|
34489
|
-
|
|
34552
|
+
path25.join(__dirname, "..", "skills", "blueprint", "presets"),
|
|
34490
34553
|
// dist/ → package root
|
|
34491
|
-
|
|
34554
|
+
path25.join(__dirname, "..", "..", "skills", "blueprint", "presets"),
|
|
34492
34555
|
// src/intake-to-build in dev
|
|
34493
|
-
|
|
34556
|
+
path25.join(process.cwd(), "skills", "blueprint", "presets")
|
|
34494
34557
|
];
|
|
34495
|
-
return candidates.find((c) =>
|
|
34558
|
+
return candidates.find((c) => fs27.existsSync(c)) ?? null;
|
|
34496
34559
|
}
|
|
34497
34560
|
function readPresetFile(dir, base) {
|
|
34498
34561
|
try {
|
|
34499
|
-
const raw = JSON.parse(
|
|
34562
|
+
const raw = JSON.parse(fs27.readFileSync(path25.join(dir, `${base}.preset.json`), "utf8"));
|
|
34500
34563
|
if (raw && typeof raw === "object" && typeof raw.presetId === "string" && raw.skeleton) return raw;
|
|
34501
34564
|
return null;
|
|
34502
34565
|
} catch {
|
|
@@ -34508,7 +34571,7 @@ function loadPresetChoice(requested, briefPreset) {
|
|
|
34508
34571
|
if (!dir) {
|
|
34509
34572
|
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." };
|
|
34510
34573
|
}
|
|
34511
|
-
const bases =
|
|
34574
|
+
const bases = fs27.readdirSync(dir).filter((f) => f.endsWith(".preset.json")).map((f) => f.replace(/\.preset\.json$/, ""));
|
|
34512
34575
|
const tryMatch = (want) => {
|
|
34513
34576
|
const norm6 = slugify2(want);
|
|
34514
34577
|
for (const base of bases) {
|
|
@@ -34539,12 +34602,12 @@ function loadPresetChoice(requested, briefPreset) {
|
|
|
34539
34602
|
if (generic) return { ok: true, preset: generic, file: "generic-client.preset.json", note: "Using the generic preset." };
|
|
34540
34603
|
return { ok: false, error: "No usable preset file was found in the library." };
|
|
34541
34604
|
}
|
|
34542
|
-
var
|
|
34605
|
+
var fs27, path25, PRESET_TOKEN_ROOTS, FLAG_ALIASES, INLINE_TOKEN_RE, ADMIN_ROLE_RE, BRIEF_CALENDAR_TYPE_MAP;
|
|
34543
34606
|
var init_plan_from_preset = __esm({
|
|
34544
34607
|
"src/intake-to-build/plan-from-preset.ts"() {
|
|
34545
34608
|
"use strict";
|
|
34546
|
-
|
|
34547
|
-
|
|
34609
|
+
fs27 = __toESM(require("fs"));
|
|
34610
|
+
path25 = __toESM(require("path"));
|
|
34548
34611
|
init_plan();
|
|
34549
34612
|
init_brief2();
|
|
34550
34613
|
init_normalizer();
|
|
@@ -34644,7 +34707,7 @@ function compute(ans, skip, asm) {
|
|
|
34644
34707
|
O.push(o);
|
|
34645
34708
|
return o;
|
|
34646
34709
|
}
|
|
34647
|
-
function
|
|
34710
|
+
function add2(id, title2, needs, assumption, fn, fix) {
|
|
34648
34711
|
var missing = needs.filter(function(n) {
|
|
34649
34712
|
return (n.share ? share(n.id) : raw(n.id)) === null;
|
|
34650
34713
|
}).map(function(n) {
|
|
@@ -34675,7 +34738,7 @@ function compute(ans, skip, asm) {
|
|
|
34675
34738
|
}
|
|
34676
34739
|
F.push({ id, title: title2, monthly: m, assumption: assumption || null, fix });
|
|
34677
34740
|
}
|
|
34678
|
-
|
|
34741
|
+
add2(
|
|
34679
34742
|
"missed",
|
|
34680
34743
|
"Calls you never get back to",
|
|
34681
34744
|
[{ id: "calls_missed" }, { id: "missed_never_return", share: true }],
|
|
@@ -34685,7 +34748,7 @@ function compute(ans, skip, asm) {
|
|
|
34685
34748
|
},
|
|
34686
34749
|
"Call answering, day and night"
|
|
34687
34750
|
);
|
|
34688
|
-
|
|
34751
|
+
add2(
|
|
34689
34752
|
"waited",
|
|
34690
34753
|
"Enquiries that waited",
|
|
34691
34754
|
[{ id: "waited_month" }],
|
|
@@ -34703,7 +34766,7 @@ function compute(ans, skip, asm) {
|
|
|
34703
34766
|
reason: "you told us more quotes were won than were sent \u2014 worth checking before this is priced"
|
|
34704
34767
|
});
|
|
34705
34768
|
} else {
|
|
34706
|
-
|
|
34769
|
+
add2(
|
|
34707
34770
|
"quotes",
|
|
34708
34771
|
"Quotes nobody chases",
|
|
34709
34772
|
[{ id: "quotes_month" }, { id: "quotes_won" }],
|
|
@@ -34714,7 +34777,7 @@ function compute(ans, skip, asm) {
|
|
|
34714
34777
|
"Follow-up that runs itself"
|
|
34715
34778
|
);
|
|
34716
34779
|
}
|
|
34717
|
-
|
|
34780
|
+
add2(
|
|
34718
34781
|
"noshow",
|
|
34719
34782
|
"No-shows nobody rebooks",
|
|
34720
34783
|
[{ id: "booked_month" }, { id: "show_rate", share: true }],
|
|
@@ -35867,13 +35930,13 @@ var init_client_intake = __esm({
|
|
|
35867
35930
|
|
|
35868
35931
|
// src/plan-store.ts
|
|
35869
35932
|
function plansDir(base = appDataDir()) {
|
|
35870
|
-
return
|
|
35933
|
+
return path26.join(base, "plans");
|
|
35871
35934
|
}
|
|
35872
35935
|
function savePlanRecord(rec, base) {
|
|
35873
35936
|
const dir = plansDir(base);
|
|
35874
|
-
|
|
35875
|
-
const file2 =
|
|
35876
|
-
|
|
35937
|
+
fs28.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
35938
|
+
const file2 = path26.join(dir, `${rec.locationId}.json`);
|
|
35939
|
+
fs28.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 });
|
|
35877
35940
|
return file2;
|
|
35878
35941
|
}
|
|
35879
35942
|
function savedPlanMatchesScope(saved, current) {
|
|
@@ -35888,7 +35951,7 @@ function savedPlanMatchesScope(saved, current) {
|
|
|
35888
35951
|
}
|
|
35889
35952
|
function readSavedPlan(locationId2, base) {
|
|
35890
35953
|
try {
|
|
35891
|
-
return JSON.parse(
|
|
35954
|
+
return JSON.parse(fs28.readFileSync(path26.join(plansDir(base), `${locationId2}.json`), "utf8"));
|
|
35892
35955
|
} catch {
|
|
35893
35956
|
return null;
|
|
35894
35957
|
}
|
|
@@ -35911,12 +35974,12 @@ function planSummary(rec) {
|
|
|
35911
35974
|
if (wfs.length) parts.push(`workflows: ${wfs.map((w) => `${w.name} (${Array.isArray(w.actions) ? w.actions.length : "?"} actions)`).join("; ")}`);
|
|
35912
35975
|
return parts.join("\n") || "(plan has no recognisable sections)";
|
|
35913
35976
|
}
|
|
35914
|
-
var
|
|
35977
|
+
var fs28, path26;
|
|
35915
35978
|
var init_plan_store = __esm({
|
|
35916
35979
|
"src/plan-store.ts"() {
|
|
35917
35980
|
"use strict";
|
|
35918
|
-
|
|
35919
|
-
|
|
35981
|
+
fs28 = __toESM(require("fs"));
|
|
35982
|
+
path26 = __toESM(require("path"));
|
|
35920
35983
|
init_credentials_store();
|
|
35921
35984
|
init_build_contract();
|
|
35922
35985
|
}
|
|
@@ -37579,7 +37642,7 @@ function launchCommand(port = 7300) {
|
|
|
37579
37642
|
return `npx -y @elitedcs/ghl-mcp@latest dashboard --port=${port}`;
|
|
37580
37643
|
}
|
|
37581
37644
|
function installingEntry() {
|
|
37582
|
-
return
|
|
37645
|
+
return path27.join(__dirname, "index.js");
|
|
37583
37646
|
}
|
|
37584
37647
|
function planLauncher(platform2, home, port = 7300, entry = installingEntry()) {
|
|
37585
37648
|
const cmd = launchCommand(port);
|
|
@@ -37587,12 +37650,12 @@ function planLauncher(platform2, home, port = 7300, entry = installingEntry()) {
|
|
|
37587
37650
|
const systemApps = "/Applications";
|
|
37588
37651
|
let base = home;
|
|
37589
37652
|
try {
|
|
37590
|
-
|
|
37653
|
+
fs29.accessSync(systemApps, fs29.constants.W_OK);
|
|
37591
37654
|
base = "";
|
|
37592
37655
|
} catch {
|
|
37593
37656
|
}
|
|
37594
|
-
const appDir = base ?
|
|
37595
|
-
const macOSDir =
|
|
37657
|
+
const appDir = base ? path27.join(home, "Applications", "Command OS.app") : path27.join(systemApps, "Command OS.app");
|
|
37658
|
+
const macOSDir = path27.join(appDir, "Contents", "MacOS");
|
|
37596
37659
|
const script = [
|
|
37597
37660
|
"#!/bin/bash",
|
|
37598
37661
|
"# GHL Command \u2014 Command OS launcher (regenerate: ghl-mcp install-launcher)",
|
|
@@ -37621,13 +37684,13 @@ function planLauncher(platform2, home, port = 7300, entry = installingEntry()) {
|
|
|
37621
37684
|
targetPath: appDir,
|
|
37622
37685
|
humanLocation: base ? "your personal Applications folder at ~/Applications (Finder \u2192 Go \u2192 Home \u2192 Applications)" : "your Applications folder \u2014 open Finder \u2192 Applications and look for \u201CCommand OS\u201D",
|
|
37623
37686
|
files: [
|
|
37624
|
-
{ path:
|
|
37625
|
-
{ path:
|
|
37687
|
+
{ path: path27.join(macOSDir, "command-os"), contents: script, executable: true },
|
|
37688
|
+
{ path: path27.join(appDir, "Contents", "Info.plist"), contents: plist, executable: false }
|
|
37626
37689
|
]
|
|
37627
37690
|
};
|
|
37628
37691
|
}
|
|
37629
37692
|
if (platform2 === "win32") {
|
|
37630
|
-
const target2 =
|
|
37693
|
+
const target2 = path27.join(home, "Desktop", "Command OS.cmd");
|
|
37631
37694
|
return {
|
|
37632
37695
|
platform: platform2,
|
|
37633
37696
|
targetPath: target2,
|
|
@@ -37640,7 +37703,7 @@ function planLauncher(platform2, home, port = 7300, entry = installingEntry()) {
|
|
|
37640
37703
|
].join("\r\n"), executable: false }]
|
|
37641
37704
|
};
|
|
37642
37705
|
}
|
|
37643
|
-
const target =
|
|
37706
|
+
const target = path27.join(home, ".local", "share", "applications", "command-os.desktop");
|
|
37644
37707
|
return {
|
|
37645
37708
|
platform: platform2,
|
|
37646
37709
|
targetPath: target,
|
|
@@ -37664,7 +37727,7 @@ function planLauncher(platform2, home, port = 7300, entry = installingEntry()) {
|
|
|
37664
37727
|
function launcherInstalled(port = 7300, platform2 = process.platform, home = os4.homedir()) {
|
|
37665
37728
|
try {
|
|
37666
37729
|
const plan = planLauncher(platform2, home, port, installingEntry());
|
|
37667
|
-
return plan.files.every((f) =>
|
|
37730
|
+
return plan.files.every((f) => fs29.existsSync(f.path));
|
|
37668
37731
|
} catch {
|
|
37669
37732
|
return false;
|
|
37670
37733
|
}
|
|
@@ -37675,8 +37738,8 @@ function installLauncher(argv = []) {
|
|
|
37675
37738
|
const plan = planLauncher(process.platform, os4.homedir(), port, installingEntry());
|
|
37676
37739
|
try {
|
|
37677
37740
|
for (const f of plan.files) {
|
|
37678
|
-
|
|
37679
|
-
|
|
37741
|
+
fs29.mkdirSync(path27.dirname(f.path), { recursive: true });
|
|
37742
|
+
fs29.writeFileSync(f.path, f.contents, { mode: f.executable ? 493 : 420 });
|
|
37680
37743
|
}
|
|
37681
37744
|
} catch (e) {
|
|
37682
37745
|
process.stderr.write(`
|
|
@@ -37696,47 +37759,47 @@ function installLauncher(argv = []) {
|
|
|
37696
37759
|
].join("\n"));
|
|
37697
37760
|
return 0;
|
|
37698
37761
|
}
|
|
37699
|
-
var
|
|
37762
|
+
var fs29, os4, path27;
|
|
37700
37763
|
var init_launcher = __esm({
|
|
37701
37764
|
"src/launcher.ts"() {
|
|
37702
37765
|
"use strict";
|
|
37703
|
-
|
|
37766
|
+
fs29 = __toESM(require("fs"));
|
|
37704
37767
|
os4 = __toESM(require("os"));
|
|
37705
|
-
|
|
37768
|
+
path27 = __toESM(require("path"));
|
|
37706
37769
|
}
|
|
37707
37770
|
});
|
|
37708
37771
|
|
|
37709
37772
|
// src/npx-cache-check.ts
|
|
37710
37773
|
function defaultNpxCacheDir(env2 = process.env, platform2 = process.platform, home = os5.homedir()) {
|
|
37711
37774
|
const configured = env2.npm_config_cache?.trim();
|
|
37712
|
-
const cache = configured ? configured : platform2 === "win32" ?
|
|
37713
|
-
return
|
|
37775
|
+
const cache = configured ? configured : platform2 === "win32" ? path28.join(env2.LOCALAPPDATA?.trim() || path28.join(home, "AppData", "Local"), "npm-cache") : path28.join(home, ".npm");
|
|
37776
|
+
return path28.join(cache, "_npx");
|
|
37714
37777
|
}
|
|
37715
37778
|
function damagedNpxDownloads(npxDir) {
|
|
37716
37779
|
let entries;
|
|
37717
37780
|
try {
|
|
37718
|
-
entries =
|
|
37781
|
+
entries = fs30.readdirSync(npxDir, { withFileTypes: true });
|
|
37719
37782
|
} catch {
|
|
37720
37783
|
return [];
|
|
37721
37784
|
}
|
|
37722
37785
|
const damaged = [];
|
|
37723
37786
|
const gone = (p) => {
|
|
37724
37787
|
try {
|
|
37725
|
-
|
|
37788
|
+
fs30.statSync(p);
|
|
37726
37789
|
return false;
|
|
37727
37790
|
} catch (e) {
|
|
37728
37791
|
return e.code === "ENOENT";
|
|
37729
37792
|
}
|
|
37730
37793
|
};
|
|
37731
37794
|
for (const entry of entries) {
|
|
37732
|
-
const dir =
|
|
37733
|
-
const pkg2 =
|
|
37795
|
+
const dir = path28.join(npxDir, entry.name);
|
|
37796
|
+
const pkg2 = path28.join(dir, PACKAGE_SUBDIR);
|
|
37734
37797
|
try {
|
|
37735
|
-
if (!
|
|
37798
|
+
if (!fs30.statSync(dir).isDirectory() || !fs30.statSync(pkg2).isDirectory()) continue;
|
|
37736
37799
|
} catch {
|
|
37737
37800
|
continue;
|
|
37738
37801
|
}
|
|
37739
|
-
if (gone(
|
|
37802
|
+
if (gone(path28.join(dir, "package.json")) || gone(path28.join(pkg2, "package.json"))) damaged.push(dir);
|
|
37740
37803
|
}
|
|
37741
37804
|
return damaged.sort();
|
|
37742
37805
|
}
|
|
@@ -37745,14 +37808,14 @@ function damagedDownloadLine(dirs) {
|
|
|
37745
37808
|
const one = dirs.length === 1;
|
|
37746
37809
|
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 ")}`;
|
|
37747
37810
|
}
|
|
37748
|
-
var
|
|
37811
|
+
var fs30, os5, path28, PACKAGE_SUBDIR;
|
|
37749
37812
|
var init_npx_cache_check = __esm({
|
|
37750
37813
|
"src/npx-cache-check.ts"() {
|
|
37751
37814
|
"use strict";
|
|
37752
|
-
|
|
37815
|
+
fs30 = __toESM(require("node:fs"));
|
|
37753
37816
|
os5 = __toESM(require("node:os"));
|
|
37754
|
-
|
|
37755
|
-
PACKAGE_SUBDIR =
|
|
37817
|
+
path28 = __toESM(require("node:path"));
|
|
37818
|
+
PACKAGE_SUBDIR = path28.join("node_modules", "@elitedcs", "ghl-mcp");
|
|
37756
37819
|
}
|
|
37757
37820
|
});
|
|
37758
37821
|
|
|
@@ -37776,7 +37839,7 @@ function defaultIO() {
|
|
|
37776
37839
|
npxCacheDir: defaultNpxCacheDir(),
|
|
37777
37840
|
out: (l) => process.stdout.write(l + "\n"),
|
|
37778
37841
|
err: (l) => process.stderr.write(l + "\n"),
|
|
37779
|
-
rename: (from, to) =>
|
|
37842
|
+
rename: (from, to) => fs31.renameSync(from, to),
|
|
37780
37843
|
sleep: (ms) => new Promise((r) => setTimeout(r, ms))
|
|
37781
37844
|
};
|
|
37782
37845
|
}
|
|
@@ -37786,32 +37849,32 @@ function candidateConfigPaths(opts) {
|
|
|
37786
37849
|
const file2 = "claude_desktop_config.json";
|
|
37787
37850
|
const candidates = [];
|
|
37788
37851
|
if (platform2 === "darwin") {
|
|
37789
|
-
candidates.push(
|
|
37852
|
+
candidates.push(path29.join(home, "Library", "Application Support", "Claude", file2));
|
|
37790
37853
|
} else if (platform2 === "win32") {
|
|
37791
|
-
const appData = opts?.appData ?? process.env.APPDATA ??
|
|
37792
|
-
candidates.push(
|
|
37793
|
-
const localAppData = opts?.localAppData ?? process.env.LOCALAPPDATA ??
|
|
37794
|
-
const packagesDir =
|
|
37854
|
+
const appData = opts?.appData ?? process.env.APPDATA ?? path29.join(home, "AppData", "Roaming");
|
|
37855
|
+
candidates.push(path29.join(appData, "Claude", file2));
|
|
37856
|
+
const localAppData = opts?.localAppData ?? process.env.LOCALAPPDATA ?? path29.join(home, "AppData", "Local");
|
|
37857
|
+
const packagesDir = path29.join(localAppData, "Packages");
|
|
37795
37858
|
try {
|
|
37796
|
-
for (const entry of
|
|
37859
|
+
for (const entry of fs31.readdirSync(packagesDir)) {
|
|
37797
37860
|
if (entry.startsWith("Claude_")) {
|
|
37798
|
-
candidates.push(
|
|
37861
|
+
candidates.push(path29.join(packagesDir, entry, "LocalCache", "Roaming", "Claude", file2));
|
|
37799
37862
|
}
|
|
37800
37863
|
}
|
|
37801
37864
|
} catch {
|
|
37802
37865
|
}
|
|
37803
37866
|
} else {
|
|
37804
|
-
candidates.push(
|
|
37867
|
+
candidates.push(path29.join(home, ".config", "Claude", file2));
|
|
37805
37868
|
}
|
|
37806
37869
|
return candidates;
|
|
37807
37870
|
}
|
|
37808
37871
|
function resolveConfigPath(explicitPath) {
|
|
37809
37872
|
if (explicitPath) {
|
|
37810
|
-
const p =
|
|
37811
|
-
return { configPath: p, exists:
|
|
37873
|
+
const p = path29.resolve(explicitPath);
|
|
37874
|
+
return { configPath: p, exists: fs31.existsSync(p) };
|
|
37812
37875
|
}
|
|
37813
37876
|
const candidates = candidateConfigPaths();
|
|
37814
|
-
const existing = candidates.filter((c) =>
|
|
37877
|
+
const existing = candidates.filter((c) => fs31.existsSync(c));
|
|
37815
37878
|
if (existing.length > 1) {
|
|
37816
37879
|
throw new InstallStop(
|
|
37817
37880
|
EXIT_REFUSED,
|
|
@@ -37828,22 +37891,22 @@ function resolveConfigPath(explicitPath) {
|
|
|
37828
37891
|
function resolveSymlinkPolicy(configPath, explicitPath, home = os6.homedir()) {
|
|
37829
37892
|
let st;
|
|
37830
37893
|
try {
|
|
37831
|
-
st =
|
|
37894
|
+
st = fs31.lstatSync(configPath);
|
|
37832
37895
|
} catch {
|
|
37833
37896
|
return configPath;
|
|
37834
37897
|
}
|
|
37835
37898
|
if (!st.isSymbolicLink()) return configPath;
|
|
37836
37899
|
let real;
|
|
37837
37900
|
try {
|
|
37838
|
-
real =
|
|
37901
|
+
real = fs31.realpathSync(configPath);
|
|
37839
37902
|
} catch {
|
|
37840
37903
|
throw new InstallStop(
|
|
37841
37904
|
EXIT_REFUSED,
|
|
37842
37905
|
`${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.`
|
|
37843
37906
|
);
|
|
37844
37907
|
}
|
|
37845
|
-
const rel =
|
|
37846
|
-
const outsideHome = rel.startsWith("..") ||
|
|
37908
|
+
const rel = path29.relative(path29.resolve(home), real);
|
|
37909
|
+
const outsideHome = rel.startsWith("..") || path29.isAbsolute(rel);
|
|
37847
37910
|
if (outsideHome && !explicitPath) {
|
|
37848
37911
|
throw new InstallStop(
|
|
37849
37912
|
EXIT_REFUSED,
|
|
@@ -37853,8 +37916,8 @@ function resolveSymlinkPolicy(configPath, explicitPath, home = os6.homedir()) {
|
|
|
37853
37916
|
return real;
|
|
37854
37917
|
}
|
|
37855
37918
|
function readConfigBytes(configPath) {
|
|
37856
|
-
const bytes =
|
|
37857
|
-
const stat =
|
|
37919
|
+
const bytes = fs31.readFileSync(configPath);
|
|
37920
|
+
const stat = fs31.statSync(configPath);
|
|
37858
37921
|
if (bytes.length >= 2 && (bytes[0] === 255 && bytes[1] === 254 || bytes[0] === 254 && bytes[1] === 255)) {
|
|
37859
37922
|
throw new InstallStop(
|
|
37860
37923
|
EXIT_REFUSED,
|
|
@@ -37928,23 +37991,23 @@ function backupStamp(now) {
|
|
|
37928
37991
|
}
|
|
37929
37992
|
function chooseBackupPath(configPath, now = /* @__PURE__ */ new Date()) {
|
|
37930
37993
|
const base = `${configPath}.bak-${backupStamp(now)}`;
|
|
37931
|
-
if (!
|
|
37994
|
+
if (!fs31.existsSync(base)) return base;
|
|
37932
37995
|
for (let i = 2; ; i++) {
|
|
37933
37996
|
const candidate = `${base}-${i}`;
|
|
37934
|
-
if (!
|
|
37997
|
+
if (!fs31.existsSync(candidate)) return candidate;
|
|
37935
37998
|
}
|
|
37936
37999
|
}
|
|
37937
38000
|
function writeVerifiedBackup(configPath, originalBytes) {
|
|
37938
38001
|
const backupPath = chooseBackupPath(configPath);
|
|
37939
38002
|
try {
|
|
37940
|
-
|
|
37941
|
-
const readBack =
|
|
38003
|
+
fs31.writeFileSync(backupPath, originalBytes);
|
|
38004
|
+
const readBack = fs31.readFileSync(backupPath);
|
|
37942
38005
|
if (!readBack.equals(originalBytes)) {
|
|
37943
38006
|
throw new Error("backup read-back did not match");
|
|
37944
38007
|
}
|
|
37945
38008
|
} catch (e) {
|
|
37946
38009
|
try {
|
|
37947
|
-
|
|
38010
|
+
fs31.rmSync(backupPath, { force: true });
|
|
37948
38011
|
} catch {
|
|
37949
38012
|
}
|
|
37950
38013
|
throw new InstallStop(
|
|
@@ -37955,7 +38018,7 @@ function writeVerifiedBackup(configPath, originalBytes) {
|
|
|
37955
38018
|
return backupPath;
|
|
37956
38019
|
}
|
|
37957
38020
|
function sha256(bytes) {
|
|
37958
|
-
return (0,
|
|
38021
|
+
return (0, import_node_crypto10.createHash)("sha256").update(bytes).digest("hex");
|
|
37959
38022
|
}
|
|
37960
38023
|
function baselineOf(bytes, stat) {
|
|
37961
38024
|
return { size: stat.size, mtimeMs: stat.mtimeMs, hash: sha256(bytes) };
|
|
@@ -37963,9 +38026,9 @@ function baselineOf(bytes, stat) {
|
|
|
37963
38026
|
function assertNotStale(configPath, baseline) {
|
|
37964
38027
|
let ok = false;
|
|
37965
38028
|
try {
|
|
37966
|
-
const st =
|
|
38029
|
+
const st = fs31.statSync(configPath);
|
|
37967
38030
|
if (st.size === baseline.size && st.mtimeMs === baseline.mtimeMs) {
|
|
37968
|
-
ok = sha256(
|
|
38031
|
+
ok = sha256(fs31.readFileSync(configPath)) === baseline.hash;
|
|
37969
38032
|
} else {
|
|
37970
38033
|
ok = false;
|
|
37971
38034
|
}
|
|
@@ -37980,14 +38043,14 @@ function assertNotStale(configPath, baseline) {
|
|
|
37980
38043
|
}
|
|
37981
38044
|
}
|
|
37982
38045
|
async function atomicReplace(opts) {
|
|
37983
|
-
const dir =
|
|
37984
|
-
const tempPath =
|
|
37985
|
-
const fd =
|
|
38046
|
+
const dir = path29.dirname(opts.configPath);
|
|
38047
|
+
const tempPath = path29.join(dir, `.${path29.basename(opts.configPath)}.tmp-${process.pid}-${(0, import_node_crypto10.randomBytes)(4).toString("hex")}`);
|
|
38048
|
+
const fd = fs31.openSync(tempPath, "w");
|
|
37986
38049
|
try {
|
|
37987
|
-
|
|
37988
|
-
|
|
38050
|
+
fs31.writeFileSync(fd, opts.newBytes);
|
|
38051
|
+
fs31.fsyncSync(fd);
|
|
37989
38052
|
} finally {
|
|
37990
|
-
|
|
38053
|
+
fs31.closeSync(fd);
|
|
37991
38054
|
}
|
|
37992
38055
|
try {
|
|
37993
38056
|
for (let attempt = 0; ; attempt++) {
|
|
@@ -38012,17 +38075,17 @@ async function atomicReplace(opts) {
|
|
|
38012
38075
|
}
|
|
38013
38076
|
} catch (e) {
|
|
38014
38077
|
try {
|
|
38015
|
-
|
|
38078
|
+
fs31.rmSync(tempPath, { force: true });
|
|
38016
38079
|
} catch {
|
|
38017
38080
|
}
|
|
38018
38081
|
throw e;
|
|
38019
38082
|
}
|
|
38020
38083
|
try {
|
|
38021
|
-
const dirFd =
|
|
38084
|
+
const dirFd = fs31.openSync(dir, "r");
|
|
38022
38085
|
try {
|
|
38023
|
-
|
|
38086
|
+
fs31.fsyncSync(dirFd);
|
|
38024
38087
|
} finally {
|
|
38025
|
-
|
|
38088
|
+
fs31.closeSync(dirFd);
|
|
38026
38089
|
}
|
|
38027
38090
|
} catch {
|
|
38028
38091
|
}
|
|
@@ -38080,7 +38143,7 @@ async function runInstall(argv, ioOverride) {
|
|
|
38080
38143
|
try {
|
|
38081
38144
|
const resolved2 = resolveConfigPath(flags.path);
|
|
38082
38145
|
const configPath = resolveSymlinkPolicy(resolved2.configPath, flags.path !== void 0);
|
|
38083
|
-
const exists =
|
|
38146
|
+
const exists = fs31.existsSync(configPath);
|
|
38084
38147
|
let read2 = null;
|
|
38085
38148
|
let outcome;
|
|
38086
38149
|
if (exists) {
|
|
@@ -38112,7 +38175,7 @@ async function runInstall(argv, ioOverride) {
|
|
|
38112
38175
|
return EXIT_OK;
|
|
38113
38176
|
}
|
|
38114
38177
|
const backupPath = exists && read2 ? writeVerifiedBackup(configPath, read2.bytes) : null;
|
|
38115
|
-
if (!exists)
|
|
38178
|
+
if (!exists) fs31.mkdirSync(path29.dirname(configPath), { recursive: true });
|
|
38116
38179
|
await atomicReplace({
|
|
38117
38180
|
configPath,
|
|
38118
38181
|
newBytes,
|
|
@@ -38131,14 +38194,14 @@ async function runInstall(argv, ioOverride) {
|
|
|
38131
38194
|
return EXIT_ABORTED;
|
|
38132
38195
|
}
|
|
38133
38196
|
}
|
|
38134
|
-
var
|
|
38197
|
+
var fs31, os6, path29, import_node_crypto10, 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;
|
|
38135
38198
|
var init_config_installer = __esm({
|
|
38136
38199
|
"src/config-installer.ts"() {
|
|
38137
38200
|
"use strict";
|
|
38138
|
-
|
|
38201
|
+
fs31 = __toESM(require("node:fs"));
|
|
38139
38202
|
os6 = __toESM(require("node:os"));
|
|
38140
|
-
|
|
38141
|
-
|
|
38203
|
+
path29 = __toESM(require("node:path"));
|
|
38204
|
+
import_node_crypto10 = require("node:crypto");
|
|
38142
38205
|
import_node_util = require("node:util");
|
|
38143
38206
|
import_json5 = __toESM(require("json5"));
|
|
38144
38207
|
init_npx_cache_check();
|
|
@@ -38171,9 +38234,9 @@ function errLine(msg3) {
|
|
|
38171
38234
|
function preflightWritable() {
|
|
38172
38235
|
try {
|
|
38173
38236
|
const dir = ensureAppDataDir();
|
|
38174
|
-
const probe =
|
|
38175
|
-
|
|
38176
|
-
|
|
38237
|
+
const probe = path30.join(dir, `.write-probe.${process.pid}.${(0, import_crypto2.randomBytes)(4).toString("hex")}`);
|
|
38238
|
+
fs32.writeFileSync(probe, "ok");
|
|
38239
|
+
fs32.unlinkSync(probe);
|
|
38177
38240
|
return true;
|
|
38178
38241
|
} catch (error) {
|
|
38179
38242
|
errLine(`Config dir is not writable: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -38442,13 +38505,13 @@ async function runCli(subcommand, argv) {
|
|
|
38442
38505
|
return EXIT_USAGE2;
|
|
38443
38506
|
}
|
|
38444
38507
|
}
|
|
38445
|
-
var import_node_util2,
|
|
38508
|
+
var import_node_util2, fs32, path30, import_crypto2, EXIT_OK2, EXIT_USAGE2, EXIT_VALIDATION, EXIT_FS, USAGE;
|
|
38446
38509
|
var init_cli = __esm({
|
|
38447
38510
|
"src/cli.ts"() {
|
|
38448
38511
|
"use strict";
|
|
38449
38512
|
import_node_util2 = require("node:util");
|
|
38450
|
-
|
|
38451
|
-
|
|
38513
|
+
fs32 = __toESM(require("fs"));
|
|
38514
|
+
path30 = __toESM(require("path"));
|
|
38452
38515
|
import_crypto2 = require("crypto");
|
|
38453
38516
|
init_ghl_client();
|
|
38454
38517
|
init_token_registry();
|
|
@@ -38994,11 +39057,11 @@ __export(intake_overlay_exports, {
|
|
|
38994
39057
|
writeOverlay: () => writeOverlay
|
|
38995
39058
|
});
|
|
38996
39059
|
function overlayPath() {
|
|
38997
|
-
return
|
|
39060
|
+
return path31.join(appDataDir(), "intake-overlay.json");
|
|
38998
39061
|
}
|
|
38999
39062
|
function readOverlay() {
|
|
39000
39063
|
try {
|
|
39001
|
-
const raw = JSON.parse(
|
|
39064
|
+
const raw = JSON.parse(fs33.readFileSync(overlayPath(), "utf8"));
|
|
39002
39065
|
if (raw && typeof raw === "object") return raw;
|
|
39003
39066
|
} catch {
|
|
39004
39067
|
}
|
|
@@ -39006,14 +39069,14 @@ function readOverlay() {
|
|
|
39006
39069
|
}
|
|
39007
39070
|
function writeOverlay(layer) {
|
|
39008
39071
|
ensureAppDataDir();
|
|
39009
|
-
|
|
39072
|
+
fs33.writeFileSync(overlayPath(), JSON.stringify(layer, null, 2), { mode: 384 });
|
|
39010
39073
|
}
|
|
39011
|
-
var
|
|
39074
|
+
var fs33, path31;
|
|
39012
39075
|
var init_intake_overlay = __esm({
|
|
39013
39076
|
"src/intake-overlay.ts"() {
|
|
39014
39077
|
"use strict";
|
|
39015
|
-
|
|
39016
|
-
|
|
39078
|
+
fs33 = __toESM(require("fs"));
|
|
39079
|
+
path31 = __toESM(require("path"));
|
|
39017
39080
|
init_credentials_store();
|
|
39018
39081
|
}
|
|
39019
39082
|
});
|
|
@@ -39661,7 +39724,7 @@ function registerAssessmentTools(server2, client) {
|
|
|
39661
39724
|
const dir = args.outDir ?? (0, import_node_path6.join)((0, import_node_os.homedir)(), "Documents", "assessments");
|
|
39662
39725
|
(0, import_node_fs6.mkdirSync)(dir, { recursive: true });
|
|
39663
39726
|
const slug3 = (state.biz || "assessment").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 40);
|
|
39664
|
-
const file2 = (0, import_node_path6.join)(dir, `${slug3}-${(0,
|
|
39727
|
+
const file2 = (0, import_node_path6.join)(dir, `${slug3}-${(0, import_node_crypto11.randomBytes)(4).toString("hex")}.html`);
|
|
39665
39728
|
(0, import_node_fs6.writeFileSync)(file2, html2, "utf8");
|
|
39666
39729
|
return {
|
|
39667
39730
|
written: file2,
|
|
@@ -39684,7 +39747,7 @@ function registerAssessmentTools(server2, client) {
|
|
|
39684
39747
|
}
|
|
39685
39748
|
);
|
|
39686
39749
|
}
|
|
39687
|
-
var import_node_util3, import_zod61,
|
|
39750
|
+
var import_node_util3, import_zod61, import_node_crypto11, import_node_fs6, import_node_os, import_node_path6, QUESTION_UNITS, AREA_TITLE, ASK, AREA_OF, ASSUMPTION_LABEL, LOOKUP, StateSchema;
|
|
39688
39751
|
var init_assessment = __esm({
|
|
39689
39752
|
"src/tools/assessment.ts"() {
|
|
39690
39753
|
"use strict";
|
|
@@ -39698,7 +39761,7 @@ var init_assessment = __esm({
|
|
|
39698
39761
|
init_engine();
|
|
39699
39762
|
init_transcript();
|
|
39700
39763
|
init_report();
|
|
39701
|
-
|
|
39764
|
+
import_node_crypto11 = require("node:crypto");
|
|
39702
39765
|
import_node_fs6 = require("node:fs");
|
|
39703
39766
|
import_node_os = require("node:os");
|
|
39704
39767
|
import_node_path6 = require("node:path");
|
|
@@ -39884,11 +39947,11 @@ function recoverStuckStages(state) {
|
|
|
39884
39947
|
return { state: { ...state, clients }, recovered };
|
|
39885
39948
|
}
|
|
39886
39949
|
function cockpitStatePath() {
|
|
39887
|
-
return
|
|
39950
|
+
return path32.join(appDataDir(), "cockpit-state.json");
|
|
39888
39951
|
}
|
|
39889
39952
|
function readCockpitState() {
|
|
39890
39953
|
try {
|
|
39891
|
-
const raw = JSON.parse(
|
|
39954
|
+
const raw = JSON.parse(fs34.readFileSync(cockpitStatePath(), "utf8"));
|
|
39892
39955
|
if (raw && raw.v === 1 && raw.clients && typeof raw.clients === "object") return raw;
|
|
39893
39956
|
} catch {
|
|
39894
39957
|
}
|
|
@@ -39896,7 +39959,7 @@ function readCockpitState() {
|
|
|
39896
39959
|
}
|
|
39897
39960
|
function writeCockpitState(state) {
|
|
39898
39961
|
ensureAppDataDir();
|
|
39899
|
-
|
|
39962
|
+
fs34.writeFileSync(cockpitStatePath(), JSON.stringify(state, null, 2), { mode: 384 });
|
|
39900
39963
|
}
|
|
39901
39964
|
function setStage(state, locationId2, stageIndex, status) {
|
|
39902
39965
|
if (!Number.isInteger(stageIndex) || stageIndex < 0 || stageIndex >= STAGES2.length) throw new Error("bad stage index");
|
|
@@ -40227,7 +40290,7 @@ function notify(message) {
|
|
|
40227
40290
|
`);
|
|
40228
40291
|
try {
|
|
40229
40292
|
ensureAppDataDir();
|
|
40230
|
-
|
|
40293
|
+
fs34.appendFileSync(path32.join(appDataDir(), "cockpit.log"), `[${(/* @__PURE__ */ new Date()).toISOString()}] ${message}
|
|
40231
40294
|
`);
|
|
40232
40295
|
} catch {
|
|
40233
40296
|
}
|
|
@@ -40297,20 +40360,20 @@ function withCockpitShell(page, screen, extraHead = "") {
|
|
|
40297
40360
|
function clientDocsDir(locationId2, base = appDataDir()) {
|
|
40298
40361
|
const safeLoc = String(locationId2).replace(/[^A-Za-z0-9_-]/g, "");
|
|
40299
40362
|
if (!safeLoc) throw new Error("no client");
|
|
40300
|
-
return
|
|
40363
|
+
return path32.join(base, "clients", safeLoc, "documents");
|
|
40301
40364
|
}
|
|
40302
40365
|
function safeDocName(name) {
|
|
40303
|
-
const cleaned =
|
|
40304
|
-
const ext =
|
|
40366
|
+
const cleaned = path32.basename(String(name ?? "")).replace(/[^A-Za-z0-9._ -]/g, "_").replace(/\s+/g, " ").trim();
|
|
40367
|
+
const ext = path32.extname(cleaned).toLowerCase();
|
|
40305
40368
|
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.");
|
|
40306
40369
|
const stem = cleaned.slice(0, cleaned.length - ext.length).slice(0, 120 - ext.length).trim();
|
|
40307
40370
|
return `${stem || "document"}${ext}`;
|
|
40308
40371
|
}
|
|
40309
40372
|
function listClientDocs(locationId2, base = appDataDir()) {
|
|
40310
40373
|
const dir = clientDocsDir(locationId2, base);
|
|
40311
|
-
if (!
|
|
40312
|
-
return
|
|
40313
|
-
const st =
|
|
40374
|
+
if (!fs34.existsSync(dir)) return [];
|
|
40375
|
+
return fs34.readdirSync(dir).filter((f) => DOC_EXTS.has(path32.extname(f).toLowerCase())).map((f) => {
|
|
40376
|
+
const st = fs34.statSync(path32.join(dir, f));
|
|
40314
40377
|
return { name: f, bytes: st.size, savedAt: st.mtime.toISOString() };
|
|
40315
40378
|
}).sort((a, b) => b.savedAt.localeCompare(a.savedAt));
|
|
40316
40379
|
}
|
|
@@ -40328,7 +40391,7 @@ function readFailureText(e) {
|
|
|
40328
40391
|
function logQuietly(message) {
|
|
40329
40392
|
try {
|
|
40330
40393
|
ensureAppDataDir();
|
|
40331
|
-
|
|
40394
|
+
fs34.appendFileSync(path32.join(appDataDir(), "cockpit.log"), `[${(/* @__PURE__ */ new Date()).toISOString()}] ${message}
|
|
40332
40395
|
`);
|
|
40333
40396
|
} catch {
|
|
40334
40397
|
}
|
|
@@ -40401,40 +40464,40 @@ function newClientFailure(error, surface = "command-os", account2) {
|
|
|
40401
40464
|
}
|
|
40402
40465
|
function insideDir(dir, p) {
|
|
40403
40466
|
try {
|
|
40404
|
-
const real =
|
|
40405
|
-
const base =
|
|
40406
|
-
return real === base || real.startsWith(base +
|
|
40467
|
+
const real = fs34.realpathSync(p);
|
|
40468
|
+
const base = fs34.realpathSync(dir);
|
|
40469
|
+
return real === base || real.startsWith(base + path32.sep);
|
|
40407
40470
|
} catch {
|
|
40408
40471
|
return false;
|
|
40409
40472
|
}
|
|
40410
40473
|
}
|
|
40411
40474
|
function uniqueDocName(dir, name) {
|
|
40412
|
-
const ext =
|
|
40475
|
+
const ext = path32.extname(name), stem = name.slice(0, name.length - ext.length);
|
|
40413
40476
|
let candidate = name;
|
|
40414
|
-
for (let n = 2;
|
|
40477
|
+
for (let n = 2; fs34.existsSync(path32.join(dir, candidate)); n++) candidate = `${stem} (${n})${ext}`;
|
|
40415
40478
|
return candidate;
|
|
40416
40479
|
}
|
|
40417
40480
|
function writeDocAtomically(dir, name, bytes) {
|
|
40418
|
-
|
|
40481
|
+
fs34.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
40419
40482
|
const finalName = uniqueDocName(dir, name);
|
|
40420
|
-
const tmp =
|
|
40421
|
-
const fd =
|
|
40483
|
+
const tmp = path32.join(dir, `.${finalName}.${process.pid}.tmp`);
|
|
40484
|
+
const fd = fs34.openSync(tmp, fs34.constants.O_WRONLY | fs34.constants.O_CREAT | fs34.constants.O_EXCL | (fs34.constants.O_NOFOLLOW ?? 0), 384);
|
|
40422
40485
|
try {
|
|
40423
|
-
|
|
40424
|
-
|
|
40486
|
+
fs34.writeSync(fd, bytes);
|
|
40487
|
+
fs34.fsyncSync(fd);
|
|
40425
40488
|
} finally {
|
|
40426
|
-
|
|
40489
|
+
fs34.closeSync(fd);
|
|
40427
40490
|
}
|
|
40428
|
-
const dest =
|
|
40491
|
+
const dest = path32.join(dir, finalName);
|
|
40429
40492
|
try {
|
|
40430
|
-
if (
|
|
40431
|
-
|
|
40493
|
+
if (fs34.lstatSync(dest).isSymbolicLink()) {
|
|
40494
|
+
fs34.unlinkSync(tmp);
|
|
40432
40495
|
throw new Error("that name is taken by a link");
|
|
40433
40496
|
}
|
|
40434
40497
|
} catch (e) {
|
|
40435
40498
|
if (e.code !== "ENOENT") throw e;
|
|
40436
40499
|
}
|
|
40437
|
-
|
|
40500
|
+
fs34.renameSync(tmp, dest);
|
|
40438
40501
|
return finalName;
|
|
40439
40502
|
}
|
|
40440
40503
|
function readableAccount(locationId2, roster = currentRosterRef(), state = readCockpitState()) {
|
|
@@ -42238,8 +42301,8 @@ ${opportunityNoteFailure?.detail ?? ""}`);
|
|
|
42238
42301
|
if (!bytes.length) throw new Error("that file is empty");
|
|
42239
42302
|
if (bytes.length > DOC_MAX_BYTES) throw new Error("that file is over 20 MB");
|
|
42240
42303
|
const dir = clientDocsDir(args.locationId);
|
|
42241
|
-
|
|
42242
|
-
if (!insideDir(
|
|
42304
|
+
fs34.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
42305
|
+
if (!insideDir(path32.join(appDataDir(), "clients"), dir)) throw new Error("This client's documents folder is not where it should be, so nothing was saved.");
|
|
42243
42306
|
const saved = writeDocAtomically(dir, name, bytes);
|
|
42244
42307
|
res.end(JSON.stringify({ ok: true, name: saved, files: listClientDocs(args.locationId) }));
|
|
42245
42308
|
} catch (e) {
|
|
@@ -42593,10 +42656,10 @@ ${opportunityNoteFailure?.detail ?? ""}`);
|
|
|
42593
42656
|
if (args.file) {
|
|
42594
42657
|
const name = safeDocName(args.file);
|
|
42595
42658
|
const dir = clientDocsDir(args.locationId);
|
|
42596
|
-
const p =
|
|
42597
|
-
if (!
|
|
42598
|
-
if (
|
|
42599
|
-
const text2 =
|
|
42659
|
+
const p = path32.join(dir, name);
|
|
42660
|
+
if (!fs34.existsSync(p)) throw new Error("that document is not saved for this client");
|
|
42661
|
+
if (fs34.lstatSync(p).isSymbolicLink() || !insideDir(dir, p)) throw new Error("that document is not saved for this client");
|
|
42662
|
+
const text2 = fs34.readFileSync(p, "utf8");
|
|
42600
42663
|
if (text2.length > 6e4) throw new Error("The limit is 60,000 characters. Paste an excerpt or shorten this text file.");
|
|
42601
42664
|
fileSource = { kind: "text", text: text2 };
|
|
42602
42665
|
}
|
|
@@ -42670,14 +42733,14 @@ ${opportunityNoteFailure?.detail ?? ""}`);
|
|
|
42670
42733
|
}
|
|
42671
42734
|
if (req.method === "GET" && /^\/brand\/[a-z0-9-]+\/screenshot\.png$/.test(url)) {
|
|
42672
42735
|
const slug3 = url.split("/")[2];
|
|
42673
|
-
const file2 =
|
|
42674
|
-
if (!
|
|
42736
|
+
const file2 = path32.join(brandDir(slug3), "screenshot.png");
|
|
42737
|
+
if (!fs34.existsSync(file2)) {
|
|
42675
42738
|
res.writeHead(404);
|
|
42676
42739
|
res.end();
|
|
42677
42740
|
return;
|
|
42678
42741
|
}
|
|
42679
42742
|
res.writeHead(200, { "Content-Type": "image/png", "Cache-Control": "no-store" });
|
|
42680
|
-
res.end(
|
|
42743
|
+
res.end(fs34.readFileSync(file2));
|
|
42681
42744
|
return;
|
|
42682
42745
|
}
|
|
42683
42746
|
if (req.method === "GET" && /^\/brand\/[a-z0-9-]+$/.test(url)) {
|
|
@@ -42686,7 +42749,7 @@ ${opportunityNoteFailure?.detail ?? ""}`);
|
|
|
42686
42749
|
const dna = houseLooks().find((d) => d.slug === slug3) ?? readBrand(slug3);
|
|
42687
42750
|
const dir = brandExists(slug3) ? brandDir(slug3) : void 0;
|
|
42688
42751
|
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8", "Cache-Control": "no-store" });
|
|
42689
|
-
res.end(brandCardHtml(dna, { screenshot: dir ?
|
|
42752
|
+
res.end(brandCardHtml(dna, { screenshot: dir ? fs34.existsSync(path32.join(dir, "screenshot.png")) : false, dir, seatToken: mayServeSeat(req, port) ? seatToken : "" }));
|
|
42690
42753
|
} catch (e) {
|
|
42691
42754
|
res.writeHead(404, { "Content-Type": "text/plain; charset=utf-8" });
|
|
42692
42755
|
res.end(e.message);
|
|
@@ -42848,7 +42911,7 @@ ${opportunityNoteFailure?.detail ?? ""}`);
|
|
|
42848
42911
|
}
|
|
42849
42912
|
try {
|
|
42850
42913
|
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
42851
|
-
res.end(
|
|
42914
|
+
res.end(fs34.readFileSync(localCopyPath(locationId2), "utf8"));
|
|
42852
42915
|
} catch {
|
|
42853
42916
|
res.end("The handoff is not prepared yet. Run Verify first.");
|
|
42854
42917
|
}
|
|
@@ -43454,7 +43517,7 @@ ${opportunityNoteFailure?.detail ?? ""}`);
|
|
|
43454
43517
|
process.on("SIGTERM", stop);
|
|
43455
43518
|
});
|
|
43456
43519
|
}
|
|
43457
|
-
var import_zod62, import_node_util4,
|
|
43520
|
+
var import_zod62, import_node_util4, fs34, path32, 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, ConnectionRequest, DisconnectRequest;
|
|
43458
43521
|
var init_dashboard = __esm({
|
|
43459
43522
|
"src/dashboard.ts"() {
|
|
43460
43523
|
"use strict";
|
|
@@ -43482,8 +43545,8 @@ var init_dashboard = __esm({
|
|
|
43482
43545
|
init_handoff_snapshot();
|
|
43483
43546
|
init_member_progress();
|
|
43484
43547
|
init_build_obligations();
|
|
43485
|
-
|
|
43486
|
-
|
|
43548
|
+
fs34 = __toESM(require("fs"));
|
|
43549
|
+
path32 = __toESM(require("path"));
|
|
43487
43550
|
http = __toESM(require("http"));
|
|
43488
43551
|
import_child_process4 = require("child_process");
|
|
43489
43552
|
init_credentials_store();
|
|
@@ -43589,8 +43652,8 @@ var init_dashboard = __esm({
|
|
|
43589
43652
|
NOT_SIGNED_IN_MSG = "This browser isn't signed in to Command OS on this computer. Open Command OS on the machine it runs on, and use the page it opens for you.";
|
|
43590
43653
|
SEAT_GUARDED_READS = ["/api/brief", "/api/brief/panel", "/api/connections", "/api/capability", "/api/client-intake", "/api/intake-documents", "/api/snapshots", "/api/agency-connection", "/api/client-preflight"];
|
|
43591
43654
|
ConnectionRequest = import_zod62.z.discriminatedUnion("system", [
|
|
43592
|
-
import_zod62.z.object({ system: import_zod62.z.literal("stripe"), key: import_zod62.z.string().min(8), label: import_zod62.z.string().optional() }).strict(),
|
|
43593
|
-
import_zod62.z.object({ system: import_zod62.z.literal("slack"), key: import_zod62.z.string().min(8), label: import_zod62.z.string().optional() }).strict(),
|
|
43655
|
+
import_zod62.z.object({ system: import_zod62.z.literal("stripe"), key: import_zod62.z.string().min(8).max(4096), label: import_zod62.z.string().optional() }).strict(),
|
|
43656
|
+
import_zod62.z.object({ system: import_zod62.z.literal("slack"), key: import_zod62.z.string().min(8).max(4096), label: import_zod62.z.string().optional() }).strict(),
|
|
43594
43657
|
import_zod62.z.object({ system: import_zod62.z.literal("google-calendar"), action: import_zod62.z.enum(["start", "finish", "status"]).default("start") }).strict()
|
|
43595
43658
|
]);
|
|
43596
43659
|
DisconnectRequest = import_zod62.z.object({ system: import_zod62.z.enum(["stripe", "slack", "google-calendar"]) }).strict();
|
|
@@ -45345,13 +45408,13 @@ function contactServiceNote(payload) {
|
|
|
45345
45408
|
function registerSmartListTools(server2, builderClient) {
|
|
45346
45409
|
if (!builderClient) return;
|
|
45347
45410
|
const client = builderClient;
|
|
45348
|
-
async function smartListRequest(method,
|
|
45349
|
-
const url = `${SMARTLIST_BASE}${
|
|
45411
|
+
async function smartListRequest(method, path41, body) {
|
|
45412
|
+
const url = `${SMARTLIST_BASE}${path41}`;
|
|
45350
45413
|
const sendsBody = body !== void 0 && (method === "POST" || method === "PUT");
|
|
45351
45414
|
const response = await builderRequest(client, method, url, sendsBody ? { body } : {});
|
|
45352
45415
|
if (!response.ok) {
|
|
45353
45416
|
const text3 = await response.text();
|
|
45354
|
-
throw new Error(`Smart Lists API Error ${response.status}: ${method} ${
|
|
45417
|
+
throw new Error(`Smart Lists API Error ${response.status}: ${method} ${path41}
|
|
45355
45418
|
${text3}`);
|
|
45356
45419
|
}
|
|
45357
45420
|
const text2 = await response.text();
|
|
@@ -45596,11 +45659,11 @@ var init_smart_lists = __esm({
|
|
|
45596
45659
|
function registerReputationTools(server2, builderClient) {
|
|
45597
45660
|
if (!builderClient) return;
|
|
45598
45661
|
const client = builderClient;
|
|
45599
|
-
async function reputationRequest(method,
|
|
45600
|
-
const response = await builderRequest(client, method, `${REPUTATION_BASE}${
|
|
45662
|
+
async function reputationRequest(method, path41) {
|
|
45663
|
+
const response = await builderRequest(client, method, `${REPUTATION_BASE}${path41}`);
|
|
45601
45664
|
if (!response.ok) {
|
|
45602
45665
|
const text3 = await response.text();
|
|
45603
|
-
throw new Error(`Reputation API Error ${response.status}: ${method} ${
|
|
45666
|
+
throw new Error(`Reputation API Error ${response.status}: ${method} ${path41}
|
|
45604
45667
|
${text3}`);
|
|
45605
45668
|
}
|
|
45606
45669
|
const text2 = await response.text();
|
|
@@ -45738,11 +45801,11 @@ var init_email_campaigns = __esm({
|
|
|
45738
45801
|
function registerMembershipTools(server2, builderClient) {
|
|
45739
45802
|
if (!builderClient) return;
|
|
45740
45803
|
const client = builderClient;
|
|
45741
|
-
async function membershipRequest(
|
|
45742
|
-
const response = await builderRequest(client, method, `${MEMBERSHIP_BASE2}${
|
|
45804
|
+
async function membershipRequest(path41, method = "GET", body) {
|
|
45805
|
+
const response = await builderRequest(client, method, `${MEMBERSHIP_BASE2}${path41}`, body ? { body } : {});
|
|
45743
45806
|
if (!response.ok) {
|
|
45744
45807
|
const text3 = await response.text();
|
|
45745
|
-
throw new Error(`Membership API Error ${response.status}: ${method} ${
|
|
45808
|
+
throw new Error(`Membership API Error ${response.status}: ${method} ${path41}
|
|
45746
45809
|
${text3}`);
|
|
45747
45810
|
}
|
|
45748
45811
|
const text2 = await response.text();
|
|
@@ -45935,21 +45998,21 @@ function isAlreadyExistsError(message) {
|
|
|
45935
45998
|
}
|
|
45936
45999
|
function registerTemplateDeployerTools(server2, client) {
|
|
45937
46000
|
const builderClient = WorkflowBuilderClient.fromEnv();
|
|
45938
|
-
const templatesDir =
|
|
46001
|
+
const templatesDir = path33.resolve(__dirname, "..", "templates");
|
|
45939
46002
|
function validateTemplatePath(templateFile) {
|
|
45940
|
-
const resolved2 =
|
|
45941
|
-
const realPath =
|
|
46003
|
+
const resolved2 = path33.resolve(templateFile);
|
|
46004
|
+
const realPath = fs35.existsSync(resolved2) ? fs35.realpathSync(resolved2) : resolved2;
|
|
45942
46005
|
const allowedDirs = [
|
|
45943
46006
|
templatesDir,
|
|
45944
|
-
|
|
45945
|
-
|
|
45946
|
-
].map((d) =>
|
|
45947
|
-
const isAllowed = allowedDirs.some((dir) => realPath.startsWith(dir +
|
|
46007
|
+
path33.resolve(process.cwd(), "templates"),
|
|
46008
|
+
path33.resolve(__dirname, "..", "..", "templates")
|
|
46009
|
+
].map((d) => fs35.existsSync(d) ? fs35.realpathSync(d) : d);
|
|
46010
|
+
const isAllowed = allowedDirs.some((dir) => realPath.startsWith(dir + path33.sep));
|
|
45948
46011
|
if (!isAllowed) {
|
|
45949
46012
|
throw new Error(`Template file must be inside a templates/ directory. Got: ${realPath}`);
|
|
45950
46013
|
}
|
|
45951
|
-
if (
|
|
45952
|
-
const otherMatch = allowedDirs.slice(1).some((d) => !
|
|
46014
|
+
if (path33.relative(allowedDirs[0], realPath).startsWith("..")) {
|
|
46015
|
+
const otherMatch = allowedDirs.slice(1).some((d) => !path33.relative(d, realPath).startsWith(".."));
|
|
45953
46016
|
if (!otherMatch) {
|
|
45954
46017
|
throw new Error(`Template path escapes allowed directory.`);
|
|
45955
46018
|
}
|
|
@@ -45964,26 +46027,26 @@ function registerTemplateDeployerTools(server2, client) {
|
|
|
45964
46027
|
try {
|
|
45965
46028
|
const dirs = [
|
|
45966
46029
|
templatesDir,
|
|
45967
|
-
|
|
45968
|
-
|
|
46030
|
+
path33.resolve(process.cwd(), "templates"),
|
|
46031
|
+
path33.resolve(__dirname, "..", "..", "templates")
|
|
45969
46032
|
];
|
|
45970
46033
|
const templates = [];
|
|
45971
46034
|
const seen = /* @__PURE__ */ new Set();
|
|
45972
46035
|
for (const dir of dirs) {
|
|
45973
|
-
if (!
|
|
45974
|
-
const files =
|
|
46036
|
+
if (!fs35.existsSync(dir)) continue;
|
|
46037
|
+
const files = fs35.readdirSync(dir).filter((f) => f.endsWith(".json"));
|
|
45975
46038
|
for (const file2 of files) {
|
|
45976
|
-
const full =
|
|
46039
|
+
const full = path33.join(dir, file2);
|
|
45977
46040
|
let real;
|
|
45978
46041
|
try {
|
|
45979
|
-
real =
|
|
46042
|
+
real = fs35.realpathSync(full);
|
|
45980
46043
|
} catch {
|
|
45981
46044
|
continue;
|
|
45982
46045
|
}
|
|
45983
46046
|
if (seen.has(real)) continue;
|
|
45984
46047
|
seen.add(real);
|
|
45985
46048
|
try {
|
|
45986
|
-
const content = JSON.parse(
|
|
46049
|
+
const content = JSON.parse(fs35.readFileSync(full, "utf-8"));
|
|
45987
46050
|
if (typeof content.templateName !== "string" || !content.templateName) continue;
|
|
45988
46051
|
const parsed2 = TemplateSchema.safeParse(content);
|
|
45989
46052
|
templates.push({
|
|
@@ -46020,7 +46083,7 @@ function registerTemplateDeployerTools(server2, client) {
|
|
|
46020
46083
|
async ({ templateFile }) => {
|
|
46021
46084
|
try {
|
|
46022
46085
|
const safePath = validateTemplatePath(templateFile);
|
|
46023
|
-
const content = JSON.parse(
|
|
46086
|
+
const content = JSON.parse(fs35.readFileSync(safePath, "utf-8"));
|
|
46024
46087
|
return {
|
|
46025
46088
|
content: [
|
|
46026
46089
|
{
|
|
@@ -46060,7 +46123,7 @@ function registerTemplateDeployerTools(server2, client) {
|
|
|
46060
46123
|
try {
|
|
46061
46124
|
const locId = client.resolveLocationId(locationId2);
|
|
46062
46125
|
const safePath = validateTemplatePath(templateFile);
|
|
46063
|
-
const template = TemplateSchema.parse(JSON.parse(
|
|
46126
|
+
const template = TemplateSchema.parse(JSON.parse(fs35.readFileSync(safePath, "utf-8")));
|
|
46064
46127
|
const resolve12 = (text2) => {
|
|
46065
46128
|
if (typeof text2 !== "string") return text2;
|
|
46066
46129
|
let result = text2;
|
|
@@ -46432,15 +46495,15 @@ ${errors.join("\n")}` : dryRun ? "\nNo errors!" : "\nNo errors \u2014 every item
|
|
|
46432
46495
|
}
|
|
46433
46496
|
);
|
|
46434
46497
|
}
|
|
46435
|
-
var import_zod71,
|
|
46498
|
+
var import_zod71, fs35, path33, TemplateSchema;
|
|
46436
46499
|
var init_template_deployer = __esm({
|
|
46437
46500
|
"src/tools/template-deployer.ts"() {
|
|
46438
46501
|
"use strict";
|
|
46439
46502
|
import_zod71 = require("zod");
|
|
46440
46503
|
init_workflow_builder_client();
|
|
46441
46504
|
init_tool_helpers();
|
|
46442
|
-
|
|
46443
|
-
|
|
46505
|
+
fs35 = __toESM(require("fs"));
|
|
46506
|
+
path33 = __toESM(require("path"));
|
|
46444
46507
|
init_workflow_builder_client();
|
|
46445
46508
|
TemplateSchema = import_zod71.z.object({
|
|
46446
46509
|
templateName: import_zod71.z.string(),
|
|
@@ -46761,10 +46824,10 @@ async function confirm(run) {
|
|
|
46761
46824
|
async function runProbes(client, loc) {
|
|
46762
46825
|
return Promise.all(
|
|
46763
46826
|
PROBES.map((probe) => confirm(async () => {
|
|
46764
|
-
const
|
|
46827
|
+
const path41 = probe.path.replace("{loc}", loc);
|
|
46765
46828
|
const params = probe.params ? Object.fromEntries(Object.entries(probe.params).map(([k, v]) => [k, substitute(v, loc)])) : void 0;
|
|
46766
46829
|
try {
|
|
46767
|
-
const raw = await client.get(
|
|
46830
|
+
const raw = await client.get(path41, { params, noRetry: true });
|
|
46768
46831
|
const count2 = countRows(raw, probe.listKeys);
|
|
46769
46832
|
return {
|
|
46770
46833
|
key: probe.key,
|
|
@@ -47040,14 +47103,14 @@ var init_checkup = __esm({
|
|
|
47040
47103
|
|
|
47041
47104
|
// src/tools/user-guide.ts
|
|
47042
47105
|
function resolveGuidePath() {
|
|
47043
|
-
const bundled =
|
|
47044
|
-
if (
|
|
47045
|
-
const devRoot =
|
|
47106
|
+
const bundled = path34.resolve(__dirname, "..", "guide", "guide.html");
|
|
47107
|
+
if (fs36.existsSync(bundled)) return bundled;
|
|
47108
|
+
const devRoot = path34.resolve(__dirname, "..", "..");
|
|
47046
47109
|
try {
|
|
47047
|
-
const pkg2 = JSON.parse(
|
|
47110
|
+
const pkg2 = JSON.parse(fs36.readFileSync(path34.join(devRoot, "package.json"), "utf8"));
|
|
47048
47111
|
if (pkg2.name === "@elitedcs/ghl-mcp") {
|
|
47049
|
-
const dev =
|
|
47050
|
-
if (
|
|
47112
|
+
const dev = path34.join(devRoot, "guide", "guide.html");
|
|
47113
|
+
if (fs36.existsSync(dev)) return dev;
|
|
47051
47114
|
}
|
|
47052
47115
|
} catch {
|
|
47053
47116
|
}
|
|
@@ -47056,7 +47119,7 @@ function resolveGuidePath() {
|
|
|
47056
47119
|
function guideContents(file2) {
|
|
47057
47120
|
let html2 = "";
|
|
47058
47121
|
try {
|
|
47059
|
-
html2 =
|
|
47122
|
+
html2 = fs36.readFileSync(file2, "utf8");
|
|
47060
47123
|
} catch {
|
|
47061
47124
|
return "The guide is installed but could not be read.";
|
|
47062
47125
|
}
|
|
@@ -47080,7 +47143,7 @@ function registerUserGuideTools(server2) {
|
|
|
47080
47143
|
{ open: import_zod73.z.boolean().optional().describe("Open the guide in the browser. Takes over the person's screen \u2014 only when they asked for it.") },
|
|
47081
47144
|
async ({ open }) => {
|
|
47082
47145
|
const guidePath = resolveGuidePath();
|
|
47083
|
-
if (!
|
|
47146
|
+
if (!fs36.existsSync(guidePath)) {
|
|
47084
47147
|
return {
|
|
47085
47148
|
content: [{
|
|
47086
47149
|
type: "text",
|
|
@@ -47113,14 +47176,14 @@ ${guidePath}`;
|
|
|
47113
47176
|
}
|
|
47114
47177
|
);
|
|
47115
47178
|
}
|
|
47116
|
-
var import_child_process6, import_zod73,
|
|
47179
|
+
var import_child_process6, import_zod73, fs36, path34;
|
|
47117
47180
|
var init_user_guide = __esm({
|
|
47118
47181
|
"src/tools/user-guide.ts"() {
|
|
47119
47182
|
"use strict";
|
|
47120
47183
|
import_child_process6 = require("child_process");
|
|
47121
47184
|
import_zod73 = require("zod");
|
|
47122
|
-
|
|
47123
|
-
|
|
47185
|
+
fs36 = __toESM(require("fs"));
|
|
47186
|
+
path34 = __toESM(require("path"));
|
|
47124
47187
|
}
|
|
47125
47188
|
});
|
|
47126
47189
|
|
|
@@ -49330,9 +49393,9 @@ async function settle(page) {
|
|
|
49330
49393
|
}
|
|
49331
49394
|
async function readTarget(target) {
|
|
49332
49395
|
if (isUrl(target)) return { html: null, url: target };
|
|
49333
|
-
const abs =
|
|
49334
|
-
if (!
|
|
49335
|
-
return { html:
|
|
49396
|
+
const abs = path35.resolve(target);
|
|
49397
|
+
if (!fs37.existsSync(abs)) throw new Error(`No file at ${abs}.`);
|
|
49398
|
+
return { html: fs37.readFileSync(abs, "utf8"), url: (0, import_node_url.pathToFileURL)(abs).toString() };
|
|
49336
49399
|
}
|
|
49337
49400
|
function slidePixels(index, png, file2, sec, brand) {
|
|
49338
49401
|
let img;
|
|
@@ -49380,7 +49443,7 @@ async function verifyDesign(opts) {
|
|
|
49380
49443
|
const { html: html2, url } = await readTarget(opts.target);
|
|
49381
49444
|
const placement = opts.placement ?? "html";
|
|
49382
49445
|
const medium = opts.medium;
|
|
49383
|
-
|
|
49446
|
+
fs37.mkdirSync(opts.outDir, { recursive: true });
|
|
49384
49447
|
const files = [];
|
|
49385
49448
|
const screenshots = [];
|
|
49386
49449
|
const at = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -49425,8 +49488,8 @@ async function verifyDesign(opts) {
|
|
|
49425
49488
|
try {
|
|
49426
49489
|
await loc.scrollIntoViewIfNeeded({ timeout: 5e3 });
|
|
49427
49490
|
const png = await loc.screenshot({ type: "png", timeout: 15e3 });
|
|
49428
|
-
|
|
49429
|
-
files.push(
|
|
49491
|
+
fs37.writeFileSync(path35.join(opts.outDir, file2), png);
|
|
49492
|
+
files.push(path35.join(opts.outDir, file2));
|
|
49430
49493
|
screenshots.push(file2);
|
|
49431
49494
|
pixels.push(slidePixels(sec.index, png, file2, sec, opts.brand));
|
|
49432
49495
|
} catch {
|
|
@@ -49458,8 +49521,8 @@ async function verifyDesign(opts) {
|
|
|
49458
49521
|
const shot = "page-1440.png";
|
|
49459
49522
|
try {
|
|
49460
49523
|
const png = await page.screenshot({ type: "png", fullPage: true, timeout: 2e4 });
|
|
49461
|
-
|
|
49462
|
-
files.push(
|
|
49524
|
+
fs37.writeFileSync(path35.join(opts.outDir, shot), png);
|
|
49525
|
+
files.push(path35.join(opts.outDir, shot));
|
|
49463
49526
|
screenshots.push(shot);
|
|
49464
49527
|
} catch {
|
|
49465
49528
|
}
|
|
@@ -49467,8 +49530,8 @@ async function verifyDesign(opts) {
|
|
|
49467
49530
|
await page.setViewportSize({ width: 390, height: 844 });
|
|
49468
49531
|
await page.evaluate(SETTLE_SCROLL_JS).catch(() => void 0);
|
|
49469
49532
|
const png = await page.screenshot({ type: "png", fullPage: true, timeout: 2e4 });
|
|
49470
|
-
|
|
49471
|
-
files.push(
|
|
49533
|
+
fs37.writeFileSync(path35.join(opts.outDir, "page-390.png"), png);
|
|
49534
|
+
files.push(path35.join(opts.outDir, "page-390.png"));
|
|
49472
49535
|
screenshots.push("page-390.png");
|
|
49473
49536
|
} catch {
|
|
49474
49537
|
}
|
|
@@ -49541,21 +49604,21 @@ async function verifyDesign(opts) {
|
|
|
49541
49604
|
screenshots
|
|
49542
49605
|
};
|
|
49543
49606
|
const checked = VerificationSchema.parse(report2);
|
|
49544
|
-
const jsonPath =
|
|
49545
|
-
|
|
49607
|
+
const jsonPath = path35.join(opts.outDir, "verification.json");
|
|
49608
|
+
fs37.writeFileSync(jsonPath, JSON.stringify(checked, null, 2) + "\n");
|
|
49546
49609
|
files.push(jsonPath);
|
|
49547
49610
|
const reportHtml = renderVerificationHtml(checked, opts.branding);
|
|
49548
|
-
const htmlPath =
|
|
49549
|
-
|
|
49611
|
+
const htmlPath = path35.join(opts.outDir, "verification.html");
|
|
49612
|
+
fs37.writeFileSync(htmlPath, reportHtml);
|
|
49550
49613
|
files.push(htmlPath);
|
|
49551
49614
|
return { report: checked, files, reportHtml };
|
|
49552
49615
|
}
|
|
49553
|
-
var
|
|
49616
|
+
var fs37, path35, import_node_url, WEB_WIDTHS, SETTLE_SCROLL_JS, FOCUS_JS;
|
|
49554
49617
|
var init_verify = __esm({
|
|
49555
49618
|
"src/command-os/design/verify.ts"() {
|
|
49556
49619
|
"use strict";
|
|
49557
|
-
|
|
49558
|
-
|
|
49620
|
+
fs37 = __toESM(require("node:fs"));
|
|
49621
|
+
path35 = __toESM(require("node:path"));
|
|
49559
49622
|
import_node_url = require("node:url");
|
|
49560
49623
|
init_lint();
|
|
49561
49624
|
init_probe();
|
|
@@ -49974,7 +50037,7 @@ function registerDesignStudioTools(server2) {
|
|
|
49974
50037
|
const { dna, logoSvg, logoPng } = deriveBrandDna(raw, { name, slug: slug3, rights: { declaration: a.rights, at: (/* @__PURE__ */ new Date()).toISOString() }, browser, firecrawl: fc });
|
|
49975
50038
|
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.");
|
|
49976
50039
|
const written = writeBrand(dna, { logoSvg, logoPng, screenshotPng: browser?.screenshotPng });
|
|
49977
|
-
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) =>
|
|
50040
|
+
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) => path36.basename(f)), notes };
|
|
49978
50041
|
}
|
|
49979
50042
|
);
|
|
49980
50043
|
safeTool(
|
|
@@ -50141,9 +50204,9 @@ function registerDesignStudioTools(server2) {
|
|
|
50141
50204
|
"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.",
|
|
50142
50205
|
{ file: import_zod76.z.string().describe("Path to the .html file."), medium: import_zod76.z.enum(MEDIA) },
|
|
50143
50206
|
async ({ file: file2, medium }) => {
|
|
50144
|
-
const abs =
|
|
50145
|
-
if (!
|
|
50146
|
-
const r = lintDesignContract(
|
|
50207
|
+
const abs = path36.resolve(file2);
|
|
50208
|
+
if (!fs38.existsSync(abs)) throw new Error(`No file at ${abs}.`);
|
|
50209
|
+
const r = lintDesignContract(fs38.readFileSync(abs, "utf8"), medium);
|
|
50147
50210
|
return { ok: r.ok, problems: r.problems, warnings: r.warnings, facts: r.facts };
|
|
50148
50211
|
}
|
|
50149
50212
|
);
|
|
@@ -50164,7 +50227,7 @@ function registerDesignStudioTools(server2) {
|
|
|
50164
50227
|
async (a) => {
|
|
50165
50228
|
const brand = a.brandSlug ? houseLooks().find((d) => d.slug === a.brandSlug) ?? readBrand(slugify(a.brandSlug)) : void 0;
|
|
50166
50229
|
const isUrl2 = /^https?:\/\//i.test(a.target);
|
|
50167
|
-
const outDir = a.outDir ?? (isUrl2 ?
|
|
50230
|
+
const outDir = a.outDir ?? (isUrl2 ? path36.resolve(`verify-${new URL(a.target).hostname.replace(/[^a-z0-9.-]/gi, "_")}`) : path36.join(path36.dirname(path36.resolve(a.target)), `verify-${path36.basename(a.target, ".html")}`));
|
|
50168
50231
|
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() });
|
|
50169
50232
|
const v = r.report;
|
|
50170
50233
|
const failed = v.rules.filter((x) => x.state === "MEASURED-FAIL").map((x) => `${x.ruleId} ${x.name}: ${x.measured ?? ""}`);
|
|
@@ -50179,7 +50242,7 @@ function registerDesignStudioTools(server2) {
|
|
|
50179
50242
|
judgments: v.rules.filter((x) => x.state === "ASSERTED").map((x) => `${x.ruleId} ${x.name}: ${x.detail ?? ""}`),
|
|
50180
50243
|
notApplicable: v.rules.filter((x) => x.state === "N-A").map((x) => `${x.ruleId}: ${x.detail ?? ""}`),
|
|
50181
50244
|
brandMatch: v.brandMatch,
|
|
50182
|
-
report:
|
|
50245
|
+
report: path36.join(outDir, "verification.html"),
|
|
50183
50246
|
files: r.files,
|
|
50184
50247
|
...a.placement === "ghl" && { builderOwned: builderOwnedRules().map((b) => `${b.rule.id} ${b.rule.name}: ${b.why}`) }
|
|
50185
50248
|
};
|
|
@@ -50207,9 +50270,9 @@ function registerDesignStudioTools(server2) {
|
|
|
50207
50270
|
if (!a.pageDataDownloadUrl && !a.file) throw new Error("Pass pageDataDownloadUrl (from get_page_full) or file.");
|
|
50208
50271
|
let page;
|
|
50209
50272
|
if (a.file) {
|
|
50210
|
-
const abs =
|
|
50211
|
-
if (!
|
|
50212
|
-
page = JSON.parse(
|
|
50273
|
+
const abs = path36.resolve(a.file);
|
|
50274
|
+
if (!fs38.existsSync(abs)) throw new Error(`No file at ${abs}.`);
|
|
50275
|
+
page = JSON.parse(fs38.readFileSync(abs, "utf8"));
|
|
50213
50276
|
} else {
|
|
50214
50277
|
const response = await fetch(a.pageDataDownloadUrl, { headers: { Accept: "application/json,*/*" } });
|
|
50215
50278
|
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.`);
|
|
@@ -50250,9 +50313,9 @@ function registerDesignStudioTools(server2) {
|
|
|
50250
50313
|
if (!a.file && !a.text) throw new Error("Pass file or text.");
|
|
50251
50314
|
let html2 = a.text ?? "";
|
|
50252
50315
|
if (a.file) {
|
|
50253
|
-
const abs =
|
|
50254
|
-
if (!
|
|
50255
|
-
html2 =
|
|
50316
|
+
const abs = path36.resolve(a.file);
|
|
50317
|
+
if (!fs38.existsSync(abs)) throw new Error(`No file at ${abs}.`);
|
|
50318
|
+
html2 = fs38.readFileSync(abs, "utf8");
|
|
50256
50319
|
}
|
|
50257
50320
|
const r = await gradeCopy(html2, claudeGenerate(a.model), { icp: a.icp });
|
|
50258
50321
|
if (!r.grade) {
|
|
@@ -50287,13 +50350,13 @@ function registerDesignStudioTools(server2) {
|
|
|
50287
50350
|
}
|
|
50288
50351
|
);
|
|
50289
50352
|
}
|
|
50290
|
-
var import_zod76,
|
|
50353
|
+
var import_zod76, fs38, path36, summarise2;
|
|
50291
50354
|
var init_design_studio = __esm({
|
|
50292
50355
|
"src/tools/design-studio.ts"() {
|
|
50293
50356
|
"use strict";
|
|
50294
50357
|
import_zod76 = require("zod");
|
|
50295
|
-
|
|
50296
|
-
|
|
50358
|
+
fs38 = __toESM(require("node:fs"));
|
|
50359
|
+
path36 = __toESM(require("node:path"));
|
|
50297
50360
|
init_tool_helpers();
|
|
50298
50361
|
init_extract();
|
|
50299
50362
|
init_extract_browser();
|
|
@@ -50553,18 +50616,18 @@ function owedByClient(owing, identities, links, nameFor2) {
|
|
|
50553
50616
|
const byCurrency = /* @__PURE__ */ new Map();
|
|
50554
50617
|
const unattributedByCurrency = /* @__PURE__ */ new Map();
|
|
50555
50618
|
let unattributedCount = 0;
|
|
50556
|
-
const
|
|
50619
|
+
const add2 = (m, currency, amount) => {
|
|
50557
50620
|
const c = currency ?? "unstated";
|
|
50558
50621
|
m.set(c, (m.get(c) ?? 0) + amount);
|
|
50559
50622
|
};
|
|
50560
50623
|
for (const o of owing) {
|
|
50561
50624
|
const tenant = tenantOfCustomer.get(o.customerId);
|
|
50562
50625
|
if (!tenant) {
|
|
50563
|
-
|
|
50626
|
+
add2(unattributedByCurrency, o.currency, o.amount);
|
|
50564
50627
|
unattributedCount += 1;
|
|
50565
50628
|
continue;
|
|
50566
50629
|
}
|
|
50567
|
-
|
|
50630
|
+
add2(byCurrency, o.currency, o.amount);
|
|
50568
50631
|
lines.push({
|
|
50569
50632
|
client: nameFor2(tenant),
|
|
50570
50633
|
amount: o.amount,
|
|
@@ -50597,7 +50660,7 @@ function registerConnectorTools(server2) {
|
|
|
50597
50660
|
"Store a connector's credentials on THIS machine so that system can be read. The key is written next to your GoHighLevel credentials, readable only by you, and is never sent anywhere, never returned by any tool, and never written to a log. Stripe answers who owes you money, what each person or company pays you and when they renew. Slack lets the morning brief and a stopped build land in the channel you already look at, and shows what is waiting unanswered in the channels the bot is in. Your own Gmail, Google Calendar and Drive need no key here: the brief asks the connectors your assistant already has.",
|
|
50598
50661
|
{
|
|
50599
50662
|
system: import_zod77.z.enum(["stripe", "slack"]).describe("The system to connect: stripe or slack."),
|
|
50600
|
-
key: import_zod77.z.string().min(8).describe("Stripe: a RESTRICTED key with read access to subscriptions, invoices and customers. Slack: a bot token (starts with xoxb-) from a Slack app you own, with chat:write to post and channels:read plus channels:history to read what is waiting. Nothing here needs a key that can do more."),
|
|
50663
|
+
key: import_zod77.z.string().min(8).max(4096).describe("Stripe: a RESTRICTED key with read access to subscriptions, invoices and customers. Slack: a bot token (starts with xoxb-) from a Slack app you own, with chat:write to post and channels:read plus channels:history to read what is waiting. Nothing here needs a key that can do more."),
|
|
50601
50664
|
label: import_zod77.z.string().optional().describe("Which account this is, so you can tell two apart later.")
|
|
50602
50665
|
},
|
|
50603
50666
|
connectSystem
|
|
@@ -51017,11 +51080,11 @@ var init_audit = __esm({
|
|
|
51017
51080
|
// src/command-os/delivery-audit/store.ts
|
|
51018
51081
|
function deliveryAuditPath() {
|
|
51019
51082
|
const override = process.env[DELIVERY_AUDIT_ENV];
|
|
51020
|
-
return override && override.trim() ? override :
|
|
51083
|
+
return override && override.trim() ? override : path37.join(appDataDir(), "delivery-audit.json");
|
|
51021
51084
|
}
|
|
51022
51085
|
function readDeliveryAudit() {
|
|
51023
51086
|
try {
|
|
51024
|
-
const parsed2 = JSON.parse(
|
|
51087
|
+
const parsed2 = JSON.parse(fs39.readFileSync(deliveryAuditPath(), "utf-8"));
|
|
51025
51088
|
if (!parsed2 || typeof parsed2 !== "object" || !parsed2.answers || typeof parsed2.answers !== "object") return { ...EMPTY3 };
|
|
51026
51089
|
return { v: 1, answers: parsed2.answers, updated_at: parsed2.updated_at };
|
|
51027
51090
|
} catch {
|
|
@@ -51032,7 +51095,7 @@ function readForWrite2() {
|
|
|
51032
51095
|
const target = deliveryAuditPath();
|
|
51033
51096
|
let raw;
|
|
51034
51097
|
try {
|
|
51035
|
-
raw =
|
|
51098
|
+
raw = fs39.readFileSync(target, "utf-8");
|
|
51036
51099
|
} catch (e) {
|
|
51037
51100
|
if (e?.code === "ENOENT") return { ...EMPTY3 };
|
|
51038
51101
|
throw new Error("Your saved audit could not be read, so nothing was changed. Nothing was lost.");
|
|
@@ -51044,7 +51107,7 @@ function readForWrite2() {
|
|
|
51044
51107
|
} catch {
|
|
51045
51108
|
const kept = `${target}.unreadable-${Date.now()}`;
|
|
51046
51109
|
try {
|
|
51047
|
-
|
|
51110
|
+
fs39.copyFileSync(target, kept);
|
|
51048
51111
|
} catch {
|
|
51049
51112
|
}
|
|
51050
51113
|
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.`);
|
|
@@ -51056,13 +51119,13 @@ function writeFile2(f) {
|
|
|
51056
51119
|
const tmp = `${target}.${process.pid}.tmp`;
|
|
51057
51120
|
let renamed = false;
|
|
51058
51121
|
try {
|
|
51059
|
-
|
|
51060
|
-
|
|
51122
|
+
fs39.writeFileSync(tmp, JSON.stringify(f, null, 2) + "\n", { encoding: "utf-8", mode: 384 });
|
|
51123
|
+
fs39.renameSync(tmp, target);
|
|
51061
51124
|
renamed = true;
|
|
51062
51125
|
} finally {
|
|
51063
51126
|
if (!renamed) {
|
|
51064
51127
|
try {
|
|
51065
|
-
|
|
51128
|
+
fs39.unlinkSync(tmp);
|
|
51066
51129
|
} catch {
|
|
51067
51130
|
}
|
|
51068
51131
|
}
|
|
@@ -51083,12 +51146,12 @@ function resetDeliveryAudit(now = () => /* @__PURE__ */ new Date()) {
|
|
|
51083
51146
|
writeFile2(next);
|
|
51084
51147
|
return next;
|
|
51085
51148
|
}
|
|
51086
|
-
var
|
|
51149
|
+
var fs39, path37, EMPTY3, DELIVERY_AUDIT_ENV;
|
|
51087
51150
|
var init_store4 = __esm({
|
|
51088
51151
|
"src/command-os/delivery-audit/store.ts"() {
|
|
51089
51152
|
"use strict";
|
|
51090
|
-
|
|
51091
|
-
|
|
51153
|
+
fs39 = __toESM(require("node:fs"));
|
|
51154
|
+
path37 = __toESM(require("node:path"));
|
|
51092
51155
|
init_credentials_store();
|
|
51093
51156
|
EMPTY3 = { v: 1, answers: {} };
|
|
51094
51157
|
DELIVERY_AUDIT_ENV = "GHL_MCP_DELIVERY_AUDIT";
|
|
@@ -51313,7 +51376,7 @@ function planFingerprint(goal, steps) {
|
|
|
51313
51376
|
goal,
|
|
51314
51377
|
steps: steps.map((s) => ({ id: s.id, tool: s.tool, targets: s.targets, creates: s.creates ?? [], args: s.args ?? {} }))
|
|
51315
51378
|
});
|
|
51316
|
-
return (0,
|
|
51379
|
+
return (0, import_node_crypto12.createHash)("sha256").update(JSON.stringify(shape)).digest("hex").slice(0, 16);
|
|
51317
51380
|
}
|
|
51318
51381
|
function checkPlan(goal, findings, steps, now = /* @__PURE__ */ new Date(), knownTools) {
|
|
51319
51382
|
const refusals = [];
|
|
@@ -51530,11 +51593,11 @@ function abandon(a, why) {
|
|
|
51530
51593
|
const stillApplied = position(a).undo;
|
|
51531
51594
|
return { ok: true, automation: { ...a, phase: "abandoned", goal: a.goal, abandonedBecause: why }, stillApplied };
|
|
51532
51595
|
}
|
|
51533
|
-
var
|
|
51596
|
+
var import_node_crypto12, isExisting, FINDINGS_STALE_AFTER_MINUTES;
|
|
51534
51597
|
var init_automation = __esm({
|
|
51535
51598
|
"src/command-os/build/automation.ts"() {
|
|
51536
51599
|
"use strict";
|
|
51537
|
-
|
|
51600
|
+
import_node_crypto12 = require("node:crypto");
|
|
51538
51601
|
init_verify_findings();
|
|
51539
51602
|
isExisting = (t) => "existing" in t;
|
|
51540
51603
|
FINDINGS_STALE_AFTER_MINUTES = 60;
|
|
@@ -51544,7 +51607,7 @@ var init_automation = __esm({
|
|
|
51544
51607
|
// src/command-os/build/store.ts
|
|
51545
51608
|
function automationsPath() {
|
|
51546
51609
|
const override = process.env[AUTOMATIONS_ENV];
|
|
51547
|
-
return override && override.trim() ? override :
|
|
51610
|
+
return override && override.trim() ? override : path38.join(appDataDir(), "automations.json");
|
|
51548
51611
|
}
|
|
51549
51612
|
function parse4(raw) {
|
|
51550
51613
|
const p = JSON.parse(raw);
|
|
@@ -51553,7 +51616,7 @@ function parse4(raw) {
|
|
|
51553
51616
|
}
|
|
51554
51617
|
function readAutomations() {
|
|
51555
51618
|
try {
|
|
51556
|
-
return parse4(
|
|
51619
|
+
return parse4(fs40.readFileSync(automationsPath(), "utf-8")).builds;
|
|
51557
51620
|
} catch {
|
|
51558
51621
|
return { ...EMPTY4.builds };
|
|
51559
51622
|
}
|
|
@@ -51562,7 +51625,7 @@ function readForWrite3() {
|
|
|
51562
51625
|
const target = automationsPath();
|
|
51563
51626
|
let raw;
|
|
51564
51627
|
try {
|
|
51565
|
-
raw =
|
|
51628
|
+
raw = fs40.readFileSync(target, "utf-8");
|
|
51566
51629
|
} catch (e) {
|
|
51567
51630
|
if (e?.code === "ENOENT") return { v: 1, builds: {} };
|
|
51568
51631
|
throw new Error("Your builds file could not be read, so nothing was changed. Nothing was lost.");
|
|
@@ -51572,7 +51635,7 @@ function readForWrite3() {
|
|
|
51572
51635
|
} catch {
|
|
51573
51636
|
const kept = `${target}.unreadable-${Date.now()}`;
|
|
51574
51637
|
try {
|
|
51575
|
-
|
|
51638
|
+
fs40.copyFileSync(target, kept);
|
|
51576
51639
|
} catch {
|
|
51577
51640
|
}
|
|
51578
51641
|
throw new Error(
|
|
@@ -51586,13 +51649,13 @@ function writeFile3(f) {
|
|
|
51586
51649
|
const tmp = `${target}.${process.pid}.tmp`;
|
|
51587
51650
|
let renamed = false;
|
|
51588
51651
|
try {
|
|
51589
|
-
|
|
51590
|
-
|
|
51652
|
+
fs40.writeFileSync(tmp, JSON.stringify(f, null, 2) + "\n", { encoding: "utf-8", mode: 384 });
|
|
51653
|
+
fs40.renameSync(tmp, target);
|
|
51591
51654
|
renamed = true;
|
|
51592
51655
|
} finally {
|
|
51593
51656
|
if (!renamed) {
|
|
51594
51657
|
try {
|
|
51595
|
-
|
|
51658
|
+
fs40.unlinkSync(tmp);
|
|
51596
51659
|
} catch {
|
|
51597
51660
|
}
|
|
51598
51661
|
}
|
|
@@ -51618,12 +51681,12 @@ function newAutomationId(now = () => /* @__PURE__ */ new Date()) {
|
|
|
51618
51681
|
}
|
|
51619
51682
|
return id;
|
|
51620
51683
|
}
|
|
51621
|
-
var
|
|
51684
|
+
var fs40, path38, AUTOMATIONS_ENV, EMPTY4;
|
|
51622
51685
|
var init_store5 = __esm({
|
|
51623
51686
|
"src/command-os/build/store.ts"() {
|
|
51624
51687
|
"use strict";
|
|
51625
|
-
|
|
51626
|
-
|
|
51688
|
+
fs40 = __toESM(require("node:fs"));
|
|
51689
|
+
path38 = __toESM(require("node:path"));
|
|
51627
51690
|
init_credentials_store();
|
|
51628
51691
|
AUTOMATIONS_ENV = "GHL_MCP_AUTOMATIONS";
|
|
51629
51692
|
EMPTY4 = { v: 1, builds: {} };
|
|
@@ -51636,7 +51699,7 @@ function isIdShapedKey(key) {
|
|
|
51636
51699
|
}
|
|
51637
51700
|
function scanArgs(args, maxNodes = MAX_ARG_NODES) {
|
|
51638
51701
|
const out = { ids: [], locations: [], unknown: [], uncheckable: [] };
|
|
51639
|
-
const
|
|
51702
|
+
const add2 = (kind, v, key) => {
|
|
51640
51703
|
const list2 = Array.isArray(v) ? v : [v];
|
|
51641
51704
|
for (const x of list2) {
|
|
51642
51705
|
const id = typeof x === "number" ? String(x) : x;
|
|
@@ -51672,28 +51735,28 @@ function scanArgs(args, maxNodes = MAX_ARG_NODES) {
|
|
|
51672
51735
|
if (k === "locationId") {
|
|
51673
51736
|
if (typeof x === "string" && x.length > 0) out.locations.push(x);
|
|
51674
51737
|
} else if (k === "traffic_weightage" && isRecord10(x)) {
|
|
51675
|
-
|
|
51738
|
+
add2("user", Object.keys(x), k);
|
|
51676
51739
|
continue;
|
|
51677
51740
|
} else if (k === "traffic_index" && Array.isArray(x)) {
|
|
51678
|
-
|
|
51741
|
+
add2("user", x.map((e) => isRecord10(e) ? e.id : void 0), k);
|
|
51679
51742
|
} else if (k === "selectedUser") {
|
|
51680
|
-
if (v.userType !== "all")
|
|
51743
|
+
if (v.userType !== "all") add2("user", x, k);
|
|
51681
51744
|
} else if (k === "field" && inFields) {
|
|
51682
51745
|
if (typeof x === "string") {
|
|
51683
51746
|
const standardKey = STANDARD_CONTACT_FIELDS.has(x.toLowerCase());
|
|
51684
|
-
if (!standardKey || isIdShaped(x))
|
|
51747
|
+
if (!standardKey || isIdShaped(x)) add2("custom_field", x, "fields[].field");
|
|
51685
51748
|
}
|
|
51686
51749
|
} else if (k === "customFields" && Array.isArray(x)) {
|
|
51687
|
-
|
|
51750
|
+
add2("custom_field", x.map((e) => isRecord10(e) ? e.id : void 0), "customFields[].id");
|
|
51688
51751
|
} else if (k === "customField" && isRecord10(x)) {
|
|
51689
|
-
|
|
51752
|
+
add2("custom_field", x.id, "customField.id");
|
|
51690
51753
|
} else if (Object.hasOwn(ID_ARG_KINDS, k)) {
|
|
51691
|
-
|
|
51754
|
+
add2(ID_ARG_KINDS[k], x, k);
|
|
51692
51755
|
} else if (isIdShapedKey(k) && !Object.hasOwn(NOT_CHECKABLE_ID_ARGS, k) && hasValue(x)) {
|
|
51693
51756
|
out.unknown.push({ key: k, value: JSON.stringify(x).slice(0, 80), ...nodeType ? { nodeType } : {} });
|
|
51694
51757
|
}
|
|
51695
51758
|
if ((k === "filterField" || k === "field" && !inFields) && typeof x === "string" && Object.hasOwn(CONDITION_FIELD_KINDS, x)) {
|
|
51696
|
-
|
|
51759
|
+
add2(CONDITION_FIELD_KINDS[x], Object.hasOwn(v, "secondValue") ? v.secondValue : v.value, `${k} ${x}`);
|
|
51697
51760
|
}
|
|
51698
51761
|
if (x && typeof x === "object") stack.push({ v: x, inFields: k === "fields" && Array.isArray(x), nodeType });
|
|
51699
51762
|
}
|
|
@@ -53770,11 +53833,11 @@ function previewShowsPlanBody(preview, planHtml) {
|
|
|
53770
53833
|
}
|
|
53771
53834
|
function bindingIdentities(plan) {
|
|
53772
53835
|
const result = [];
|
|
53773
|
-
const
|
|
53774
|
-
for (const u of planUsers(plan))
|
|
53836
|
+
const add2 = (type, ref, name, identity2 = name) => result.push({ ref, type, name, identity: `${type}:${norm5(identity2)}` });
|
|
53837
|
+
for (const u of planUsers(plan)) add2("user", u.ref, userDisplayName(u), u.email);
|
|
53775
53838
|
for (const p of plan.pipelines ?? []) {
|
|
53776
|
-
|
|
53777
|
-
for (const stage of p.stages)
|
|
53839
|
+
add2("pipeline", p.ref, p.name);
|
|
53840
|
+
for (const stage of p.stages) add2("stage", stage.ref, stage.name, `${p.ref}:${p.name}:${stage.name}`);
|
|
53778
53841
|
}
|
|
53779
53842
|
for (const [type, objects] of [
|
|
53780
53843
|
["field", plan.customFields],
|
|
@@ -53785,7 +53848,7 @@ function bindingIdentities(plan) {
|
|
|
53785
53848
|
["workflow", plan.workflows],
|
|
53786
53849
|
["email_template", planEmailTemplates(plan)],
|
|
53787
53850
|
["sms_template", planSmsTemplates(plan)]
|
|
53788
|
-
]) for (const obj of objects ?? [])
|
|
53851
|
+
]) for (const obj of objects ?? []) add2(type, obj.ref, obj.name);
|
|
53789
53852
|
return result;
|
|
53790
53853
|
}
|
|
53791
53854
|
function matchingPriorBindings(plan, previous, ids = {}) {
|
|
@@ -55011,13 +55074,13 @@ ${text3}`);
|
|
|
55011
55074
|
};
|
|
55012
55075
|
}
|
|
55013
55076
|
function makePipelineApi(builderClient) {
|
|
55014
|
-
return async (method,
|
|
55015
|
-
const url = `https://backend.leadconnectorhq.com/opportunities/pipelines${
|
|
55077
|
+
return async (method, path41, body) => {
|
|
55078
|
+
const url = `https://backend.leadconnectorhq.com/opportunities/pipelines${path41}`;
|
|
55016
55079
|
const sendsBody = body !== void 0 && (method === "POST" || method === "PUT");
|
|
55017
55080
|
const response = await builderRequest(builderClient, method, url, sendsBody ? { body } : {});
|
|
55018
55081
|
if (!response.ok) {
|
|
55019
55082
|
const text3 = await response.text();
|
|
55020
|
-
throw new Error(`Pipeline API ${response.status}: ${method} ${
|
|
55083
|
+
throw new Error(`Pipeline API ${response.status}: ${method} ${path41}
|
|
55021
55084
|
${text3}`);
|
|
55022
55085
|
}
|
|
55023
55086
|
const text2 = await response.text();
|
|
@@ -55030,8 +55093,8 @@ ${text3}`);
|
|
|
55030
55093
|
};
|
|
55031
55094
|
}
|
|
55032
55095
|
function makeFunnelApi(builderClient) {
|
|
55033
|
-
return async (method,
|
|
55034
|
-
const url = `https://backend.leadconnectorhq.com/funnels${
|
|
55096
|
+
return async (method, path41, body) => {
|
|
55097
|
+
const url = `https://backend.leadconnectorhq.com/funnels${path41}`;
|
|
55035
55098
|
const sendsBody = body !== void 0 && (method === "POST" || method === "PUT");
|
|
55036
55099
|
const response = await builderRequest(builderClient, method, url, {
|
|
55037
55100
|
...sendsBody ? { body } : {},
|
|
@@ -55044,7 +55107,7 @@ function makeFunnelApi(builderClient) {
|
|
|
55044
55107
|
});
|
|
55045
55108
|
if (!response.ok) {
|
|
55046
55109
|
const text3 = await response.text();
|
|
55047
|
-
throw new Error(`Funnel API ${response.status}: ${method} ${
|
|
55110
|
+
throw new Error(`Funnel API ${response.status}: ${method} ${path41}
|
|
55048
55111
|
${text3.slice(0, 300)}`);
|
|
55049
55112
|
}
|
|
55050
55113
|
const text2 = await response.text();
|
|
@@ -56359,7 +56422,7 @@ function registerAllTools(server2, client, registry2, mcpVersion, env2 = process
|
|
|
56359
56422
|
return findings.map((finding) => ({ finding, because: `"${finding.id}" cannot be checked: no saved key for sub-account ${loc}. Register it (register_location) or point the server at it first.` }));
|
|
56360
56423
|
}
|
|
56361
56424
|
const reader = token2 ? new GHLClient({ apiKey: token2.apiKey, locationId: loc }) : client;
|
|
56362
|
-
return refuseUnseenFindings2((
|
|
56425
|
+
return refuseUnseenFindings2((path41, params) => reader.get(path41, params ? { params } : void 0), loc, findings);
|
|
56363
56426
|
}, () => client.defaultLocationId);
|
|
56364
56427
|
registerClientEngagementTools(wrap(CLIENT_ENGAGEMENTS_MODULE), registry2);
|
|
56365
56428
|
registerAuditReportTools(wrap(AUDIT_REPORT_MODULE));
|
|
@@ -56779,8 +56842,8 @@ __export(index_exports, {
|
|
|
56779
56842
|
});
|
|
56780
56843
|
module.exports = __toCommonJS(index_exports);
|
|
56781
56844
|
var dotenv2 = __toESM(require("dotenv"));
|
|
56782
|
-
var
|
|
56783
|
-
var
|
|
56845
|
+
var path40 = __toESM(require("path"));
|
|
56846
|
+
var fs42 = __toESM(require("fs"));
|
|
56784
56847
|
var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
56785
56848
|
|
|
56786
56849
|
// src/version-compare.ts
|
|
@@ -56851,12 +56914,12 @@ init_setup_tool();
|
|
|
56851
56914
|
var import_zod87 = require("zod");
|
|
56852
56915
|
|
|
56853
56916
|
// src/skill-installer.ts
|
|
56854
|
-
var
|
|
56855
|
-
var
|
|
56856
|
-
var
|
|
56917
|
+
var import_node_crypto13 = require("node:crypto");
|
|
56918
|
+
var fs41 = __toESM(require("node:fs"));
|
|
56919
|
+
var path39 = __toESM(require("node:path"));
|
|
56857
56920
|
var os7 = __toESM(require("node:os"));
|
|
56858
56921
|
function sha2562(buf) {
|
|
56859
|
-
return (0,
|
|
56922
|
+
return (0, import_node_crypto13.createHash)("sha256").update(buf).digest("hex");
|
|
56860
56923
|
}
|
|
56861
56924
|
function isOlderRelease(candidate, installed) {
|
|
56862
56925
|
const parts = (v) => v.trim().split(".").map((n) => Number.parseInt(n, 10));
|
|
@@ -56868,9 +56931,9 @@ function isOlderRelease(candidate, installed) {
|
|
|
56868
56931
|
return false;
|
|
56869
56932
|
}
|
|
56870
56933
|
function bundledSkillsDir(baseDir) {
|
|
56871
|
-
const candidate =
|
|
56934
|
+
const candidate = path39.resolve(baseDir, "..", "skills");
|
|
56872
56935
|
try {
|
|
56873
|
-
return
|
|
56936
|
+
return fs41.statSync(candidate).isDirectory() ? candidate : null;
|
|
56874
56937
|
} catch {
|
|
56875
56938
|
return null;
|
|
56876
56939
|
}
|
|
@@ -56878,10 +56941,10 @@ function bundledSkillsDir(baseDir) {
|
|
|
56878
56941
|
function listFiles(root) {
|
|
56879
56942
|
const out = [];
|
|
56880
56943
|
const walk = (dir) => {
|
|
56881
|
-
for (const entry of
|
|
56882
|
-
const full =
|
|
56944
|
+
for (const entry of fs41.readdirSync(dir, { withFileTypes: true })) {
|
|
56945
|
+
const full = path39.join(dir, entry.name);
|
|
56883
56946
|
if (entry.isDirectory()) walk(full);
|
|
56884
|
-
else if (entry.isFile()) out.push(
|
|
56947
|
+
else if (entry.isFile()) out.push(path39.relative(root, full));
|
|
56885
56948
|
}
|
|
56886
56949
|
};
|
|
56887
56950
|
walk(root);
|
|
@@ -56889,7 +56952,7 @@ function listFiles(root) {
|
|
|
56889
56952
|
}
|
|
56890
56953
|
function readMarker(markerPath) {
|
|
56891
56954
|
try {
|
|
56892
|
-
const parsed2 = JSON.parse(
|
|
56955
|
+
const parsed2 = JSON.parse(fs41.readFileSync(markerPath, "utf8"));
|
|
56893
56956
|
return parsed2 && typeof parsed2 === "object" && parsed2.files ? parsed2 : null;
|
|
56894
56957
|
} catch {
|
|
56895
56958
|
return null;
|
|
@@ -56903,35 +56966,35 @@ function installBundledSkills(opts) {
|
|
|
56903
56966
|
skippedOlderPackage: [],
|
|
56904
56967
|
unchanged: [],
|
|
56905
56968
|
errors: [],
|
|
56906
|
-
targetDir: opts.targetDir ??
|
|
56969
|
+
targetDir: opts.targetDir ?? path39.join(os7.homedir(), ".claude", "skills"),
|
|
56907
56970
|
bundledDir: null
|
|
56908
56971
|
};
|
|
56909
56972
|
const bundled = bundledSkillsDir(opts.baseDir);
|
|
56910
56973
|
result.bundledDir = bundled;
|
|
56911
56974
|
if (!bundled) return result;
|
|
56912
56975
|
try {
|
|
56913
|
-
|
|
56976
|
+
fs41.mkdirSync(result.targetDir, { recursive: true });
|
|
56914
56977
|
} catch (e) {
|
|
56915
56978
|
result.errors.push(`cannot create ${result.targetDir}: ${String(e)}`);
|
|
56916
56979
|
return result;
|
|
56917
56980
|
}
|
|
56918
|
-
const markerPath =
|
|
56981
|
+
const markerPath = path39.join(result.targetDir, ".ghl-command-skills.json");
|
|
56919
56982
|
const marker = readMarker(markerPath) ?? { packageVersion: "", files: {} };
|
|
56920
56983
|
const newMarker = { packageVersion: opts.packageVersion, files: { ...marker.files } };
|
|
56921
56984
|
for (const rel of listFiles(bundled)) {
|
|
56922
56985
|
try {
|
|
56923
|
-
const src =
|
|
56924
|
-
const dest =
|
|
56986
|
+
const src = fs41.readFileSync(path39.join(bundled, rel));
|
|
56987
|
+
const dest = path39.join(result.targetDir, rel);
|
|
56925
56988
|
const srcHash = sha2562(src);
|
|
56926
56989
|
let destBuf = null;
|
|
56927
56990
|
try {
|
|
56928
|
-
destBuf =
|
|
56991
|
+
destBuf = fs41.readFileSync(dest);
|
|
56929
56992
|
} catch {
|
|
56930
56993
|
destBuf = null;
|
|
56931
56994
|
}
|
|
56932
56995
|
if (destBuf === null) {
|
|
56933
|
-
|
|
56934
|
-
|
|
56996
|
+
fs41.mkdirSync(path39.dirname(dest), { recursive: true });
|
|
56997
|
+
fs41.writeFileSync(dest, src);
|
|
56935
56998
|
newMarker.files[rel] = srcHash;
|
|
56936
56999
|
result.installed.push(rel);
|
|
56937
57000
|
continue;
|
|
@@ -56948,7 +57011,7 @@ function installBundledSkills(opts) {
|
|
|
56948
57011
|
newMarker.packageVersion = marker.packageVersion;
|
|
56949
57012
|
result.skippedOlderPackage.push(rel);
|
|
56950
57013
|
} else if (lastWritten && destHash === lastWritten) {
|
|
56951
|
-
|
|
57014
|
+
fs41.writeFileSync(dest, src);
|
|
56952
57015
|
newMarker.files[rel] = srcHash;
|
|
56953
57016
|
result.updated.push(rel);
|
|
56954
57017
|
} else {
|
|
@@ -56959,7 +57022,7 @@ function installBundledSkills(opts) {
|
|
|
56959
57022
|
}
|
|
56960
57023
|
}
|
|
56961
57024
|
try {
|
|
56962
|
-
|
|
57025
|
+
fs41.writeFileSync(markerPath, JSON.stringify(newMarker, null, 2));
|
|
56963
57026
|
} catch (e) {
|
|
56964
57027
|
result.errors.push(`marker write failed: ${String(e)}`);
|
|
56965
57028
|
}
|
|
@@ -57115,7 +57178,7 @@ dotenv2.config();
|
|
|
57115
57178
|
setPkgVersion(pkg.version);
|
|
57116
57179
|
{
|
|
57117
57180
|
const configDirOverride = process.env.GHL_MCP_CONFIG_DIR?.trim();
|
|
57118
|
-
if (configDirOverride && !
|
|
57181
|
+
if (configDirOverride && !path40.isAbsolute(configDirOverride)) {
|
|
57119
57182
|
process.stderr.write(
|
|
57120
57183
|
`[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.
|
|
57121
57184
|
`
|
|
@@ -57128,20 +57191,20 @@ process.on("unhandledRejection", (reason3) => {
|
|
|
57128
57191
|
`);
|
|
57129
57192
|
});
|
|
57130
57193
|
function hardenSecretFilePerms() {
|
|
57131
|
-
const repoDir =
|
|
57194
|
+
const repoDir = path40.resolve(__dirname, "..");
|
|
57132
57195
|
const candidates = [
|
|
57133
|
-
{ file:
|
|
57196
|
+
{ file: path40.join(repoDir, "start-mcp.sh"), mode: 448 },
|
|
57134
57197
|
// Legacy registry location (pre-migration); new location lives in app-data.
|
|
57135
|
-
{ file:
|
|
57198
|
+
{ file: path40.join(repoDir, ".ghl-tokens.json"), mode: 384 },
|
|
57136
57199
|
{ file: tokenRegistryPath(), mode: 384 }
|
|
57137
57200
|
];
|
|
57138
57201
|
for (const { file: file2, mode } of candidates) {
|
|
57139
57202
|
let current;
|
|
57140
57203
|
try {
|
|
57141
|
-
if (!
|
|
57142
|
-
current =
|
|
57204
|
+
if (!fs42.existsSync(file2)) continue;
|
|
57205
|
+
current = fs42.statSync(file2).mode & 511;
|
|
57143
57206
|
if (current !== mode) {
|
|
57144
|
-
|
|
57207
|
+
fs42.chmodSync(file2, mode);
|
|
57145
57208
|
}
|
|
57146
57209
|
} catch (error) {
|
|
57147
57210
|
const message = error instanceof Error ? error.message : String(error);
|