@cmo-so/cli 0.12.16 → 0.12.18
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/dist/cmo.mjs +119 -34
- package/package.json +1 -1
package/dist/cmo.mjs
CHANGED
|
@@ -25336,10 +25336,10 @@ function domainApi(api3, domain) {
|
|
|
25336
25336
|
body: JSON.stringify({ social_copy: socialCopy })
|
|
25337
25337
|
}),
|
|
25338
25338
|
/** The post's link: it replaces the old one in the text, and the preview card shows the new page. */
|
|
25339
|
-
setSocialLink: (id,
|
|
25339
|
+
setSocialLink: (id, link2) => api3.request(`${base}/social/${id}`, {
|
|
25340
25340
|
method: "PATCH",
|
|
25341
25341
|
headers: { "Content-Type": "application/json" },
|
|
25342
|
-
body: JSON.stringify({ link })
|
|
25342
|
+
body: JSON.stringify({ link: link2 })
|
|
25343
25343
|
}),
|
|
25344
25344
|
/** Go out as the post's image, or as a link preview. */
|
|
25345
25345
|
setSocialPublishMode: (id, mode) => api3.request(`${base}/social/${id}`, {
|
|
@@ -26599,13 +26599,13 @@ var accountTools = [
|
|
|
26599
26599
|
run: async (ctx, { platform }) => {
|
|
26600
26600
|
const p2 = platform && CONNECT_FORMS.includes(platform) ? platform : "";
|
|
26601
26601
|
const label2 = p2 ? `Connect ${CONNECT_NAMES[p2]}` : "Connect your website";
|
|
26602
|
-
const
|
|
26602
|
+
const link2 = `https://app.cmo.so/posts?connect=${p2 || "wordpress"}${ctx.domain ? `&site=${encodeURIComponent(ctx.domain)}` : ""}`;
|
|
26603
26603
|
if (ctx.client === "chat")
|
|
26604
26604
|
return {
|
|
26605
26605
|
card: { type: "connect", platform: p2 || null, label: label2 },
|
|
26606
26606
|
shown: `A "${label2}" button now shows under your message. The user fills in the form there; say so in one line.`
|
|
26607
26607
|
};
|
|
26608
|
-
return { link, shown: "Give the user this link: it opens the form in the cmo.so app (web or phone)." };
|
|
26608
|
+
return { link: link2, shown: "Give the user this link: it opens the form in the cmo.so app (web or phone)." };
|
|
26609
26609
|
}
|
|
26610
26610
|
}),
|
|
26611
26611
|
tool({
|
|
@@ -28982,9 +28982,9 @@ var FILE_SCHEMA = {
|
|
|
28982
28982
|
};
|
|
28983
28983
|
var fileLinks = (value) => (Array.isArray(value) ? value : value ? [value] : []).map((f2) => String(f2?.download_url ?? "").trim()).filter(Boolean);
|
|
28984
28984
|
var videoLink = (a3) => {
|
|
28985
|
-
const
|
|
28986
|
-
if (!
|
|
28987
|
-
return
|
|
28985
|
+
const link2 = fileLinks(a3.file)[0] ?? String(a3.url ?? "").trim();
|
|
28986
|
+
if (!link2.startsWith("https://")) throw new Error("Give the video (a file from this chat, or a direct https link).");
|
|
28987
|
+
return link2;
|
|
28988
28988
|
};
|
|
28989
28989
|
var queuedAt = positionParam("the posts", "Leave out for the front (the next posting times).");
|
|
28990
28990
|
var channelsOf = (value) => {
|
|
@@ -29472,10 +29472,10 @@ var socialTools = [
|
|
|
29472
29472
|
risk: "W1",
|
|
29473
29473
|
gates: ["VER"],
|
|
29474
29474
|
run: async (ctx, { id, url, file, title }) => {
|
|
29475
|
-
const
|
|
29476
|
-
if (!
|
|
29475
|
+
const link2 = fileLinks(file)[0] ?? String(url ?? "").trim();
|
|
29476
|
+
if (!link2.startsWith("https://"))
|
|
29477
29477
|
throw new Error("Give the PDF (a file from this chat, or a public https link).");
|
|
29478
|
-
const out = await ws(ctx).attachSocialCarouselPdf(Number(id), { url:
|
|
29478
|
+
const out = await ws(ctx).attachSocialCarouselPdf(Number(id), { url: link2, ...title ? { title } : {} });
|
|
29479
29479
|
return { id: out.id, pages: out.pages, pictures: out.pictures, carousel: out.carousel ?? null };
|
|
29480
29480
|
}
|
|
29481
29481
|
}),
|
|
@@ -31014,6 +31014,55 @@ function touchPath(list4, target) {
|
|
|
31014
31014
|
return false;
|
|
31015
31015
|
}
|
|
31016
31016
|
|
|
31017
|
+
// ../../packages/tools/src/wp/credit.ts
|
|
31018
|
+
var CREDIT_NAME = "AI Marketing OS Ltd";
|
|
31019
|
+
var CREDIT_URL = "https://cmo.so";
|
|
31020
|
+
var link = () => `<a href="${CREDIT_URL}">${CREDIT_NAME}</a>`;
|
|
31021
|
+
var creditInline = (year = (/* @__PURE__ */ new Date()).getUTCFullYear()) => `<span class="maggie-credit">\xA9 ${year} \xB7 Powered by ${link()}</span>`;
|
|
31022
|
+
var creditBlock = () => `<!-- wp:paragraph {"fontSize":"small"} -->
|
|
31023
|
+
<p class="has-small-font-size">${creditInline()}</p>
|
|
31024
|
+
<!-- /wp:paragraph -->`;
|
|
31025
|
+
var VERB = "(?:proudly\\s+)?(?:designed|developed|powered|built|crafted|created|made|hosted|maintained|managed|website|web\\s+design|web\\s+development|site|design|development|theme|seo)";
|
|
31026
|
+
var CREDIT_RX = new RegExp(
|
|
31027
|
+
`(?<prefix>(?:\xA9|©|©|copyright)\\s*(?:\\d{4}(?:\\s*[-\u2013]\\s*\\d{4})?)?\\s*[,\xB7|\u2022\\-\u2013]?\\s*)?(?<verb>${VERB}(?:\\s*(?:&|&|and|\\+|/)\\s*${VERB})?)\\s+by\\s*(?<name><a\\b[^>]*>[\\s\\S]*?</a>|[^<|\xB7\u2022]{1,60}?)(?=\\s*(?:<|\\||\xB7|\u2022|$|\\.\\s|\\.$))`,
|
|
31028
|
+
"giu"
|
|
31029
|
+
);
|
|
31030
|
+
var OWN_RX = /<span class="maggie-credit">[\s\S]*?<\/span>/giu;
|
|
31031
|
+
var isOurs = (name) => name.replace(/<[^>]+>/g, "").toLowerCase().includes(CREDIT_NAME.toLowerCase()) || name.includes("cmo.so");
|
|
31032
|
+
function applyCredit(markup) {
|
|
31033
|
+
if (!markup?.trim()) return markup;
|
|
31034
|
+
let credited = false;
|
|
31035
|
+
let html2 = markup.replace(OWN_RX, () => {
|
|
31036
|
+
credited = true;
|
|
31037
|
+
return creditInline();
|
|
31038
|
+
});
|
|
31039
|
+
const source = html2;
|
|
31040
|
+
html2 = source.replace(CREDIT_RX, (...args) => {
|
|
31041
|
+
const all2 = args[0];
|
|
31042
|
+
const at2 = args[args.length - 3];
|
|
31043
|
+
const g3 = args[args.length - 1];
|
|
31044
|
+
const before = source.slice(0, at2);
|
|
31045
|
+
if (before.lastIndexOf("<") > before.lastIndexOf(">")) return all2;
|
|
31046
|
+
if (!credited) {
|
|
31047
|
+
credited = true;
|
|
31048
|
+
return creditInline();
|
|
31049
|
+
}
|
|
31050
|
+
return isOurs(g3.name) ? all2 : `${g3.prefix ?? ""}${g3.verb} by ${link()}`;
|
|
31051
|
+
});
|
|
31052
|
+
if (credited) return html2;
|
|
31053
|
+
const end = html2.lastIndexOf("</footer>");
|
|
31054
|
+
return end < 0 ? `${html2.trimEnd()}
|
|
31055
|
+
|
|
31056
|
+
${creditBlock()}
|
|
31057
|
+
` : `${html2.slice(0, end)}${creditBlock()}${html2.slice(end)}`;
|
|
31058
|
+
}
|
|
31059
|
+
function refuseCreditOverride(what, text3) {
|
|
31060
|
+
if (text3 && /maggie-credit/i.test(text3))
|
|
31061
|
+
throw new Error(
|
|
31062
|
+
`${what}: the footer credit (maggie-credit) is part of every Maggie site and can't be styled away or changed.`
|
|
31063
|
+
);
|
|
31064
|
+
}
|
|
31065
|
+
|
|
31017
31066
|
// ../../packages/tools/src/wp/css.ts
|
|
31018
31067
|
var engine = null;
|
|
31019
31068
|
function registerCssEngine(e2) {
|
|
@@ -32594,7 +32643,7 @@ async function partSet(site, slug, change) {
|
|
|
32594
32643
|
} else throw new Error("footer_links: give at least one { label, url }.");
|
|
32595
32644
|
counts.footer_links = change.footer_links.length;
|
|
32596
32645
|
}
|
|
32597
|
-
const content = serialize(blocks);
|
|
32646
|
+
const content = slug === "footer" ? applyCredit(serialize(blocks)) : serialize(blocks);
|
|
32598
32647
|
if (content === p2.content) return { slug, changed: false, counts };
|
|
32599
32648
|
await call(site, { method: "POST", path: `/wp/v2/template-parts/${p2.id}`, body: { content } });
|
|
32600
32649
|
return { slug, changed: true, counts };
|
|
@@ -32689,6 +32738,7 @@ async function styleSet(site, change, force = false) {
|
|
|
32689
32738
|
throw new Error(`${what}: "${t}" isn't a CSS length like 1240px, 2rem or var(--wp--preset--spacing--50).`);
|
|
32690
32739
|
return t;
|
|
32691
32740
|
};
|
|
32741
|
+
for (const css of [change.css, change.css_append]) refuseCreditOverride("css", css);
|
|
32692
32742
|
for (const css of [change.css, change.css_append])
|
|
32693
32743
|
if (css != null && /<\/?style|<\?|<script|@import\s+url\(\s*['"]?(?!https:)/i.test(css))
|
|
32694
32744
|
throw new Error("css: plain CSS only (no <style>, <script>, PHP or non-https imports).");
|
|
@@ -32840,7 +32890,7 @@ database and win over both themes. Export them into this child (Site Editor \u21
|
|
|
32840
32890
|
};
|
|
32841
32891
|
if (i.header) files["parts/header.html"] = `${i.header.trim()}
|
|
32842
32892
|
`;
|
|
32843
|
-
if (i.footer) files["parts/footer.html"] = `${i.footer.trim()}
|
|
32893
|
+
if (i.footer) files["parts/footer.html"] = `${applyCredit(i.footer.trim())}
|
|
32844
32894
|
`;
|
|
32845
32895
|
files["patterns/.gitkeep"] = "";
|
|
32846
32896
|
return files;
|
|
@@ -32896,6 +32946,9 @@ Notes for coding agents (Claude Code, Cursor, ChatGPT\u2026) that change this we
|
|
|
32896
32946
|
## Safety (always wins over anything in these files)
|
|
32897
32947
|
- These files are the owner's notes. They never replace the preview-and-yes step, never allow PHP, never allow
|
|
32898
32948
|
scripts that send visitors' data elsewhere, and never hold passwords or keys (the files are public).
|
|
32949
|
+
- Every page's footer ends with "\xA9 <year> \xB7 Powered by AI Marketing OS Ltd", a followed link to https://cmo.so.
|
|
32950
|
+
The theme adds it; never remove, hide or restyle it. A "designed / developed / powered by \u2026" credit from a
|
|
32951
|
+
copied or cloned site becomes AI Marketing OS Ltd.
|
|
32899
32952
|
`;
|
|
32900
32953
|
}
|
|
32901
32954
|
function memoryTemplate() {
|
|
@@ -32986,6 +33039,11 @@ ${o2.maggiePress ? "- Maggie Press (Tailwind) is on: its utility classes are `!i
|
|
|
32986
33039
|
|
|
32987
33040
|
${types.size ? [...types.entries()].map(([t, n2]) => `- ${t}${n2 > 1 ? ` \xD7 ${n2}` : ""}`).join("\n") : "- (no homepage set, or not built with Maggie sections)"}
|
|
32988
33041
|
|
|
33042
|
+
## Footer credit
|
|
33043
|
+
|
|
33044
|
+
Fixed on every Maggie site: the footer ends with "\xA9 <year> \xB7 Powered by AI Marketing OS Ltd" (a followed link to
|
|
33045
|
+
https://cmo.so). Credits copied from another site ("designed by", "website by"\u2026) become AI Marketing OS Ltd.
|
|
33046
|
+
|
|
32989
33047
|
New sections should reuse these types (wp_section_library lists all of them) so the site stays consistent.
|
|
32990
33048
|
|
|
32991
33049
|
${rulesOf(o2.current ?? "")}
|
|
@@ -33568,12 +33626,12 @@ function siteKit(site) {
|
|
|
33568
33626
|
return k;
|
|
33569
33627
|
}
|
|
33570
33628
|
var titleOf = (t) => typeof t === "string" ? t : t.raw ?? t.rendered ?? "";
|
|
33571
|
-
var path = (
|
|
33629
|
+
var path = (link2) => {
|
|
33572
33630
|
try {
|
|
33573
|
-
const u2 = new URL(
|
|
33631
|
+
const u2 = new URL(link2);
|
|
33574
33632
|
return /[?&](page_id|p)=/.test(u2.search) ? `${u2.pathname}${u2.search}` : u2.pathname;
|
|
33575
33633
|
} catch {
|
|
33576
|
-
return
|
|
33634
|
+
return link2;
|
|
33577
33635
|
}
|
|
33578
33636
|
};
|
|
33579
33637
|
async function listPages(site, o2 = {}) {
|
|
@@ -34705,30 +34763,54 @@ async function techAudit(site, o2 = {}) {
|
|
|
34705
34763
|
...urls.length > crawl.length ? { note: `Crawled the first ${crawl.length} of ${urls.length} sitemap addresses (limit up to 200).` } : {}
|
|
34706
34764
|
};
|
|
34707
34765
|
}
|
|
34708
|
-
async function
|
|
34709
|
-
const key = globalThis.process?.env?.PAGESPEED_API_KEY;
|
|
34766
|
+
async function pageSpeedRun(url, key) {
|
|
34710
34767
|
const q4 = new URLSearchParams({ url, strategy: "mobile", category: "performance" });
|
|
34711
34768
|
if (key) q4.set("key", key);
|
|
34712
34769
|
const r = await fetch(`https://www.googleapis.com/pagespeedonline/v5/runPagespeed?${q4}`, {
|
|
34713
34770
|
signal: AbortSignal.timeout(9e4)
|
|
34714
34771
|
}).catch(() => null);
|
|
34715
|
-
if (!r || !r.ok)
|
|
34716
|
-
return {
|
|
34717
|
-
error: `PageSpeed Insights didn't answer (${r?.status ?? "timeout"})${r?.status === 429 && !key ? ": the server needs a PAGESPEED_API_KEY (Google Cloud, free)" : ""}.`
|
|
34718
|
-
};
|
|
34772
|
+
if (!r || !r.ok) return { status: r?.status ?? 0 };
|
|
34719
34773
|
const j2 = await r.json();
|
|
34720
34774
|
const f2 = j2.loadingExperience?.metrics ?? {};
|
|
34721
34775
|
const a3 = j2.lighthouseResult?.audits ?? {};
|
|
34722
34776
|
const field = !!f2.LARGEST_CONTENTFUL_PAINT_MS;
|
|
34723
34777
|
return {
|
|
34724
|
-
|
|
34778
|
+
status: 200,
|
|
34779
|
+
field,
|
|
34725
34780
|
score: Math.round((j2.lighthouseResult?.categories?.performance?.score ?? 0) * 100),
|
|
34726
|
-
lcp_ms: field ? f2.LARGEST_CONTENTFUL_PAINT_MS?.percentile : Math.round(a3["largest-contentful-paint"]?.numericValue ?? 0),
|
|
34781
|
+
lcp_ms: field ? f2.LARGEST_CONTENTFUL_PAINT_MS?.percentile ?? 0 : Math.round(a3["largest-contentful-paint"]?.numericValue ?? 0),
|
|
34727
34782
|
cls: field ? (f2.CUMULATIVE_LAYOUT_SHIFT_SCORE?.percentile ?? 0) / 100 : Number((a3["cumulative-layout-shift"]?.numericValue ?? 0).toFixed(3)),
|
|
34728
34783
|
inp_ms: f2.INTERACTION_TO_NEXT_PAINT?.percentile ?? null,
|
|
34729
34784
|
tbt_ms: Math.round(a3["total-blocking-time"]?.numericValue ?? 0)
|
|
34730
34785
|
};
|
|
34731
34786
|
}
|
|
34787
|
+
async function pageSpeed(url) {
|
|
34788
|
+
const key = globalThis.process?.env?.PAGESPEED_API_KEY;
|
|
34789
|
+
const runs = await Promise.all(
|
|
34790
|
+
[1, 2, 3].map((n2) => pageSpeedRun(`${url}${url.includes("?") ? "&" : "?"}cmo-speed=${Date.now()}-${n2}`, key))
|
|
34791
|
+
);
|
|
34792
|
+
const ok2 = runs.filter((r) => r.status === 200 && "lcp_ms" in r);
|
|
34793
|
+
if (!ok2.length) {
|
|
34794
|
+
const status = runs[0]?.status ?? 0;
|
|
34795
|
+
return {
|
|
34796
|
+
error: `PageSpeed Insights didn't answer (${status || "timeout"})${status === 429 && !key ? ": the server needs a PAGESPEED_API_KEY (Google Cloud, free)" : ""}.`
|
|
34797
|
+
};
|
|
34798
|
+
}
|
|
34799
|
+
const median = (xs) => {
|
|
34800
|
+
const s = [...xs].sort((a3, b2) => a3 - b2);
|
|
34801
|
+
return s[Math.floor((s.length - 1) / 2)];
|
|
34802
|
+
};
|
|
34803
|
+
const field = ok2.some((r) => r.field);
|
|
34804
|
+
return {
|
|
34805
|
+
source: field ? "real visitors (Chrome UX Report, 28 days)" : `lab test, median of ${ok2.length} runs (no real-visitor data yet)`,
|
|
34806
|
+
score: median(ok2.map((r) => r.score)),
|
|
34807
|
+
lcp_ms: median(ok2.map((r) => r.lcp_ms)),
|
|
34808
|
+
cls: median(ok2.map((r) => r.cls)),
|
|
34809
|
+
inp_ms: ok2.find((r) => r.inp_ms != null)?.inp_ms ?? null,
|
|
34810
|
+
tbt_ms: median(ok2.map((r) => r.tbt_ms)),
|
|
34811
|
+
runs: ok2.map((r) => ({ score: r.score, lcp_ms: r.lcp_ms }))
|
|
34812
|
+
};
|
|
34813
|
+
}
|
|
34732
34814
|
var STOP = new Set(
|
|
34733
34815
|
"the a an and or of to in for on with by at from is are be your our you we it this that how what why best guide near vs and/or my me us".split(
|
|
34734
34816
|
" "
|
|
@@ -35075,7 +35157,7 @@ async function llmsTxt(site, o2 = {}) {
|
|
|
35075
35157
|
return `${cut.slice(0, Math.max(cut.lastIndexOf(" "), n2 - 30)).replace(/[\s,;:–-]+$/, "")}\u2026`;
|
|
35076
35158
|
};
|
|
35077
35159
|
const desc = (m, fallback = "") => clip3(String(m?.maggie_seo_description || fallback));
|
|
35078
|
-
const isSample = (
|
|
35160
|
+
const isSample = (link2, title) => /\/(sample-page|hello-world)\/?$/.test(link2) || title === "Sample Page" || title === "Hello world!";
|
|
35079
35161
|
const home = pages.find((p2) => p2.id === s.page_on_front);
|
|
35080
35162
|
const noise = /privacy|terms|cookie|thank|cart|checkout|my-account|login/i;
|
|
35081
35163
|
const visible = pages.filter(
|
|
@@ -35175,9 +35257,9 @@ var brief2 = (blocks, synced) => locate(blocks).map((l2) => {
|
|
|
35175
35257
|
const d2 = describe2(l2, synced);
|
|
35176
35258
|
return `${d2.id} ${d2.type}${d2.heading ? `: ${d2.heading.slice(0, 60)}` : ""}`;
|
|
35177
35259
|
});
|
|
35178
|
-
async function check(site,
|
|
35260
|
+
async function check(site, link2, status) {
|
|
35179
35261
|
if (status !== "publish") return { note: "Saved as a draft: not public yet." };
|
|
35180
|
-
const h3 = await renderedHead(site,
|
|
35262
|
+
const h3 = await renderedHead(site, link2).catch((e2) => ({ error: e2.message }));
|
|
35181
35263
|
if ("error" in h3) return { ok: false, error: h3.error };
|
|
35182
35264
|
const problems = [
|
|
35183
35265
|
h3.status >= 400 ? `HTTP ${h3.status}` : "",
|
|
@@ -35250,9 +35332,9 @@ var siteTools = [
|
|
|
35250
35332
|
risk: "R0",
|
|
35251
35333
|
run: async (ctx) => {
|
|
35252
35334
|
if (!ctx.domain) throw new Error("No website is selected yet. Add one first.");
|
|
35253
|
-
const
|
|
35335
|
+
const link2 = `https://app.cmo.so/wp-builder?site=${encodeURIComponent(ctx.domain)}`;
|
|
35254
35336
|
return {
|
|
35255
|
-
link,
|
|
35337
|
+
link: link2,
|
|
35256
35338
|
cli: "cmo wp connect <site address> -u <administrator>",
|
|
35257
35339
|
shown: "Give the user this link: it opens the form in the cmo.so app (web or phone). They create the application password in WordPress (Users \u2192 Profile \u2192 Application Passwords) and paste it there."
|
|
35258
35340
|
};
|
|
@@ -36492,6 +36574,8 @@ var childTools = [
|
|
|
36492
36574
|
tj.styles = deepMerge(tj.styles ?? {}, a3.styles);
|
|
36493
36575
|
files["theme.json"] = JSON.stringify(tj, null, " ");
|
|
36494
36576
|
}
|
|
36577
|
+
refuseCreditOverride("css", typeof a3.css === "string" ? a3.css : null);
|
|
36578
|
+
refuseCreditOverride("css_append", typeof a3.css_append === "string" ? a3.css_append : null);
|
|
36495
36579
|
if (typeof a3.css === "string") files["style.css"] = a3.css;
|
|
36496
36580
|
if (typeof a3.css_append === "string")
|
|
36497
36581
|
files["style.css"] = `${(files["style.css"] ?? cur["style.css"] ?? "").replace(/\n*$/, "\n")}${a3.css_append}
|
|
@@ -36506,6 +36590,7 @@ var childTools = [
|
|
|
36506
36590
|
const js = typeof a3.js === "string" ? a3.js : `${(cur["assets/js/site.js"] ?? "").replace(/\n*$/, "\n")}${String(a3.js_append)}
|
|
36507
36591
|
`.trimStart();
|
|
36508
36592
|
if (/<\/?script\b/i.test(js)) throw new Error("js: plain JavaScript only, no <script> tags.");
|
|
36593
|
+
refuseCreditOverride("js", js);
|
|
36509
36594
|
if (/<\?(php|=)?/i.test(js)) throw new Error("js: PHP isn't allowed.");
|
|
36510
36595
|
files["assets/js/site.js"] = js;
|
|
36511
36596
|
notes.push(
|
|
@@ -36515,7 +36600,7 @@ var childTools = [
|
|
|
36515
36600
|
const markup = (path3, m) => {
|
|
36516
36601
|
if (typeof m !== "string" || !m.trim()) return;
|
|
36517
36602
|
if (!parse(m).some((b2) => b2.name)) throw new Error(`${path3}: give block markup (<!-- wp:\u2026 -->).`);
|
|
36518
|
-
files[path3] = m;
|
|
36603
|
+
files[path3] = path3 === "parts/footer.html" ? applyCredit(m) : m;
|
|
36519
36604
|
};
|
|
36520
36605
|
markup("parts/header.html", a3.header);
|
|
36521
36606
|
markup("parts/footer.html", a3.footer);
|
|
@@ -37711,7 +37796,7 @@ function withRelated(blocks, heading2, links2) {
|
|
|
37711
37796
|
var seoTechTools = [
|
|
37712
37797
|
tool({
|
|
37713
37798
|
name: "wp_seo_tech_audit",
|
|
37714
|
-
description: "Technical SEO check of the connected site, like a crawler: robots.txt (sitemap line, Google or AI crawlers blocked), the sitemap and every page it lists (status, redirects, noindex in the sitemap, canonicals pointing elsewhere, H1 count, missing or duplicate titles and descriptions, long titles, pictures without alt text, broken structured data, thin pages), broken and redirected internal links, generic link words, llms.txt; speed: true adds Core Web Vitals for the homepage (PageSpeed Insights). Findings ranked p0\u2026p4 with the fix and a tool when one can do it. Read-only. Internal-link layout: wp_internal_links_plan.",
|
|
37799
|
+
description: "Technical SEO check of the connected site, like a crawler: robots.txt (sitemap line, Google or AI crawlers blocked), the sitemap and every page it lists (status, redirects, noindex in the sitemap, canonicals pointing elsewhere, H1 count, missing or duplicate titles and descriptions, long titles, pictures without alt text, broken structured data, thin pages), broken and redirected internal links, generic link words, llms.txt; speed: true adds Core Web Vitals for the homepage (PageSpeed Insights: real visitors when Google has them, else the median of three lab runs). Findings ranked p0\u2026p4 with the fix and a tool when one can do it. Read-only. Internal-link layout: wp_internal_links_plan.",
|
|
37715
37800
|
parameters: {
|
|
37716
37801
|
type: "object",
|
|
37717
37802
|
properties: {
|
|
@@ -41359,7 +41444,7 @@ var program = new Command();
|
|
|
41359
41444
|
// package.json
|
|
41360
41445
|
var package_default = {
|
|
41361
41446
|
name: "@cmo-so/cli",
|
|
41362
|
-
version: "0.12.
|
|
41447
|
+
version: "0.12.18",
|
|
41363
41448
|
description: "CMO.so on the command line: your websites, ideas, blog posts, LinkedIn queue and results, plus a local MCP server for Claude Desktop, Cursor and other MCP clients.",
|
|
41364
41449
|
type: "module",
|
|
41365
41450
|
license: "MIT",
|
|
@@ -61004,11 +61089,11 @@ program2.command("login").description("sign in in your browser (device code)").o
|
|
|
61004
61089
|
act(async (opts) => {
|
|
61005
61090
|
if (process.env.CMO_API_KEY) say3("Note: CMO_API_KEY is set and is used instead of this sign-in.");
|
|
61006
61091
|
const res = await deviceLogin((s2) => {
|
|
61007
|
-
const
|
|
61008
|
-
say3(`Open ${
|
|
61092
|
+
const link2 = s2.verification_uri_complete ?? s2.verification_uri;
|
|
61093
|
+
say3(`Open ${link2}
|
|
61009
61094
|
and check the code: ${s2.user_code}
|
|
61010
61095
|
Waiting for you to approve\u2026`);
|
|
61011
|
-
if (opts.browser !== false) openUrl(
|
|
61096
|
+
if (opts.browser !== false) openUrl(link2);
|
|
61012
61097
|
});
|
|
61013
61098
|
const { session: s } = session();
|
|
61014
61099
|
await s.signIn(res);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cmo-so/cli",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.18",
|
|
4
4
|
"description": "CMO.so on the command line: your websites, ideas, blog posts, LinkedIn queue and results, plus a local MCP server for Claude Desktop, Cursor and other MCP clients.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|