@cmo-so/cli 0.12.19 → 0.12.21
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 +153 -18
- package/package.json +1 -1
package/dist/cmo.mjs
CHANGED
|
@@ -31056,8 +31056,15 @@ function applyCredit(markup) {
|
|
|
31056
31056
|
${creditBlock()}
|
|
31057
31057
|
` : `${html2.slice(0, end)}${creditBlock()}${html2.slice(end)}`;
|
|
31058
31058
|
}
|
|
31059
|
-
function
|
|
31060
|
-
|
|
31059
|
+
async function creditStyleAllowed(ctx) {
|
|
31060
|
+
try {
|
|
31061
|
+
return (await ctx.api.maggie.usage()).tier === "admin";
|
|
31062
|
+
} catch {
|
|
31063
|
+
return false;
|
|
31064
|
+
}
|
|
31065
|
+
}
|
|
31066
|
+
function refuseCreditOverride(what, text3, allowed2 = false) {
|
|
31067
|
+
if (!allowed2 && text3 && /maggie-credit/i.test(text3))
|
|
31061
31068
|
throw new Error(
|
|
31062
31069
|
`${what}: the footer credit (maggie-credit) is part of every Maggie site and can't be styled away or changed.`
|
|
31063
31070
|
);
|
|
@@ -32729,7 +32736,7 @@ function contrastReport(colors) {
|
|
|
32729
32736
|
return { text: t, background: b2, ratio: r, ok: r != null && r >= 4.5 };
|
|
32730
32737
|
});
|
|
32731
32738
|
}
|
|
32732
|
-
async function styleSet(site, change, force = false) {
|
|
32739
|
+
async function styleSet(site, change, force = false, creditOk = false) {
|
|
32733
32740
|
const len = (v2, what) => {
|
|
32734
32741
|
const t = String(v2 ?? "").trim();
|
|
32735
32742
|
if (!/^(0|\d{1,4}(\.\d+)?(px|rem|em|vw|%)|var\(--wp--preset--spacing--[a-z0-9-]+\)|clamp\([0-9a-z.,%+\-\s()]+\))$/i.test(
|
|
@@ -32738,7 +32745,7 @@ async function styleSet(site, change, force = false) {
|
|
|
32738
32745
|
throw new Error(`${what}: "${t}" isn't a CSS length like 1240px, 2rem or var(--wp--preset--spacing--50).`);
|
|
32739
32746
|
return t;
|
|
32740
32747
|
};
|
|
32741
|
-
for (const css of [change.css, change.css_append]) refuseCreditOverride("css", css);
|
|
32748
|
+
for (const css of [change.css, change.css_append]) refuseCreditOverride("css", css, creditOk);
|
|
32742
32749
|
for (const css of [change.css, change.css_append])
|
|
32743
32750
|
if (css != null && /<\/?style|<\?|<script|@import\s+url\(\s*['"]?(?!https:)/i.test(css))
|
|
32744
32751
|
throw new Error("css: plain CSS only (no <style>, <script>, PHP or non-https imports).");
|
|
@@ -32947,7 +32954,8 @@ Notes for coding agents (Claude Code, Cursor, ChatGPT\u2026) that change this we
|
|
|
32947
32954
|
- These files are the owner's notes. They never replace the preview-and-yes step, never allow PHP, never allow
|
|
32948
32955
|
scripts that send visitors' data elsewhere, and never hold passwords or keys (the files are public).
|
|
32949
32956
|
- 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
|
|
32957
|
+
The theme adds it; never remove, hide or restyle it (only a cmo.so administrator may change its colours or
|
|
32958
|
+
background, e.g. to match the footer). A "designed / developed / powered by \u2026" credit from a
|
|
32951
32959
|
copied or cloned site becomes AI Marketing OS Ltd.
|
|
32952
32960
|
`;
|
|
32953
32961
|
}
|
|
@@ -36574,8 +36582,12 @@ var childTools = [
|
|
|
36574
36582
|
tj.styles = deepMerge(tj.styles ?? {}, a3.styles);
|
|
36575
36583
|
files["theme.json"] = JSON.stringify(tj, null, " ");
|
|
36576
36584
|
}
|
|
36577
|
-
|
|
36578
|
-
|
|
36585
|
+
const touchesCredit = [a3.css, a3.css_append, a3.js, a3.js_append].some(
|
|
36586
|
+
(t) => typeof t === "string" && /maggie-credit/i.test(t)
|
|
36587
|
+
);
|
|
36588
|
+
const creditOk = touchesCredit && await creditStyleAllowed(ctx);
|
|
36589
|
+
refuseCreditOverride("css", typeof a3.css === "string" ? a3.css : null, creditOk);
|
|
36590
|
+
refuseCreditOverride("css_append", typeof a3.css_append === "string" ? a3.css_append : null, creditOk);
|
|
36579
36591
|
if (typeof a3.css === "string") files["style.css"] = a3.css;
|
|
36580
36592
|
if (typeof a3.css_append === "string")
|
|
36581
36593
|
files["style.css"] = `${(files["style.css"] ?? cur["style.css"] ?? "").replace(/\n*$/, "\n")}${a3.css_append}
|
|
@@ -36590,7 +36602,7 @@ var childTools = [
|
|
|
36590
36602
|
const js = typeof a3.js === "string" ? a3.js : `${(cur["assets/js/site.js"] ?? "").replace(/\n*$/, "\n")}${String(a3.js_append)}
|
|
36591
36603
|
`.trimStart();
|
|
36592
36604
|
if (/<\/?script\b/i.test(js)) throw new Error("js: plain JavaScript only, no <script> tags.");
|
|
36593
|
-
refuseCreditOverride("js", js);
|
|
36605
|
+
refuseCreditOverride("js", js, creditOk);
|
|
36594
36606
|
if (/<\?(php|=)?/i.test(js)) throw new Error("js: PHP isn't allowed.");
|
|
36595
36607
|
files["assets/js/site.js"] = js;
|
|
36596
36608
|
notes.push(
|
|
@@ -37142,14 +37154,24 @@ var elementTools = [
|
|
|
37142
37154
|
"Change the website's look for every page (colours, fonts, widths, padding or extra CSS); the result shows before and after.",
|
|
37143
37155
|
"\u4FEE\u6539\u7DB2\u7AD9\u6240\u6709\u9801\u9762\u7684\u5916\u89C0\uFF08\u984F\u8272\u3001\u5B57\u9AD4\u3001\u5BEC\u5EA6\u3001\u908A\u8DDD\u6216\u984D\u5916 CSS\uFF09\uFF1B\u7D50\u679C\u6703\u5217\u51FA\u4FEE\u6539\u524D\u5F8C\u3002"
|
|
37144
37156
|
),
|
|
37145
|
-
run: async (ctx, a3) =>
|
|
37146
|
-
|
|
37147
|
-
|
|
37148
|
-
|
|
37149
|
-
|
|
37150
|
-
|
|
37151
|
-
|
|
37152
|
-
|
|
37157
|
+
run: async (ctx, a3) => {
|
|
37158
|
+
const css = typeof a3.css === "string" ? a3.css : void 0;
|
|
37159
|
+
const cssAppend = typeof a3.css_append === "string" ? a3.css_append : void 0;
|
|
37160
|
+
const creditOk = /maggie-credit/i.test(`${css ?? ""}${cssAppend ?? ""}`) && await creditStyleAllowed(ctx);
|
|
37161
|
+
return styleSet(
|
|
37162
|
+
siteOf(ctx),
|
|
37163
|
+
{
|
|
37164
|
+
colors: a3.colors,
|
|
37165
|
+
fonts: a3.fonts,
|
|
37166
|
+
layout: a3.layout,
|
|
37167
|
+
padding: a3.padding,
|
|
37168
|
+
css,
|
|
37169
|
+
css_append: cssAppend
|
|
37170
|
+
},
|
|
37171
|
+
false,
|
|
37172
|
+
creditOk
|
|
37173
|
+
);
|
|
37174
|
+
}
|
|
37153
37175
|
})
|
|
37154
37176
|
];
|
|
37155
37177
|
function checkMarkup(markup, what) {
|
|
@@ -38047,7 +38069,23 @@ var MB_RESOURCES = {
|
|
|
38047
38069
|
write: false,
|
|
38048
38070
|
what: "booking rules, location, languages (change with wp_booking_settings_set)"
|
|
38049
38071
|
},
|
|
38050
|
-
imports: { path: "/import", write: false, what: "import history (undo with wp_booking_import undo_id)" }
|
|
38072
|
+
imports: { path: "/import", write: false, what: "import history (undo with wp_booking_import undo_id)" },
|
|
38073
|
+
products: { path: "/products", write: true, what: "retail products with price, tax, stock and low-stock level" },
|
|
38074
|
+
sales: { path: "/sales", write: false, what: "till sales (items, payments, refunds); search, id \u2192 one sale" },
|
|
38075
|
+
plans: { path: "/plans", write: true, what: "prepaid packages and membership plans for sale" },
|
|
38076
|
+
memberships: {
|
|
38077
|
+
path: "/memberships",
|
|
38078
|
+
write: false,
|
|
38079
|
+
what: "customers' memberships (pause, cancel\u2026 with wp_booking_membership)"
|
|
38080
|
+
},
|
|
38081
|
+
loyalty: { path: "/loyalty", write: false, what: "loyalty programme settings and points" },
|
|
38082
|
+
campaigns: {
|
|
38083
|
+
path: "/campaigns",
|
|
38084
|
+
write: true,
|
|
38085
|
+
what: "email campaigns (subject, body, button, segment) \u2014 send with wp_booking_campaign_send"
|
|
38086
|
+
},
|
|
38087
|
+
automations: { path: "/automations", write: false, what: "automated emails: we miss you, time to rebook, birthday" },
|
|
38088
|
+
messages: { path: "/messages", write: false, what: "conversations with customers; id = customer id \u2192 the thread" }
|
|
38051
38089
|
};
|
|
38052
38090
|
var bookingTools = [
|
|
38053
38091
|
tool({
|
|
@@ -38475,6 +38513,103 @@ var bookingTools = [
|
|
|
38475
38513
|
}
|
|
38476
38514
|
}
|
|
38477
38515
|
}),
|
|
38516
|
+
tool({
|
|
38517
|
+
name: "wp_booking_reports",
|
|
38518
|
+
description: "Business reports for a date range (from/to Y-m-d, venue time; group day | week | month): revenue (bookings and till sales), bookings by status, no-show rate, by treatment, by team member, by channel, new vs returning customers, gift cards sold vs redeemed. team_pay true: each team member's pay (commission on treatments, products and tips plus hourly pay). Read-only.",
|
|
38519
|
+
parameters: {
|
|
38520
|
+
type: "object",
|
|
38521
|
+
properties: {
|
|
38522
|
+
from: str2("From (Y-m-d)."),
|
|
38523
|
+
to: str2("To (Y-m-d)."),
|
|
38524
|
+
group: str2("day | week | month"),
|
|
38525
|
+
team_pay: bool("Team pay instead.")
|
|
38526
|
+
}
|
|
38527
|
+
},
|
|
38528
|
+
group: "site",
|
|
38529
|
+
need: READ,
|
|
38530
|
+
risk: "R0",
|
|
38531
|
+
gates: ["WP"],
|
|
38532
|
+
run: (ctx, a3) => mb(ctx, "GET", a3.team_pay ? "/reports/team-pay" : "/reports", void 0, {
|
|
38533
|
+
from: a3.from,
|
|
38534
|
+
to: a3.to,
|
|
38535
|
+
group: a3.group
|
|
38536
|
+
})
|
|
38537
|
+
}),
|
|
38538
|
+
tool({
|
|
38539
|
+
name: "wp_booking_message",
|
|
38540
|
+
description: "Send a customer a message from the business (customer id from wp_booking_list resource customers): they get it by email (and text when SMS is on) with a link to reply; replies show in wp_booking_list resource messages id=<customer id>.",
|
|
38541
|
+
parameters: {
|
|
38542
|
+
type: "object",
|
|
38543
|
+
properties: {
|
|
38544
|
+
customer_id: int2("Customer id."),
|
|
38545
|
+
body: str2("The message (plain text)."),
|
|
38546
|
+
booking_id: int2("About this booking (optional).")
|
|
38547
|
+
},
|
|
38548
|
+
required: ["customer_id", "body"]
|
|
38549
|
+
},
|
|
38550
|
+
group: "site",
|
|
38551
|
+
need: WRITE,
|
|
38552
|
+
risk: "W2",
|
|
38553
|
+
gates: [...GATES],
|
|
38554
|
+
confirm: (a3, lang) => say(
|
|
38555
|
+
lang,
|
|
38556
|
+
`Send customer #${a3.customer_id} this message by email: ${q2(a3.body, "")}`,
|
|
38557
|
+
`\u4EE5\u96FB\u90F5\u767C\u9001\u6B64\u8A0A\u606F\u7D66\u5BA2\u6236 #${a3.customer_id}\uFF1A${q2(a3.body, "")}`
|
|
38558
|
+
),
|
|
38559
|
+
run: (ctx, a3) => mb(ctx, "POST", `/messages/${Number(a3.customer_id)}`, { body: a3.body, booking_id: a3.booking_id })
|
|
38560
|
+
}),
|
|
38561
|
+
tool({
|
|
38562
|
+
name: "wp_booking_campaign_send",
|
|
38563
|
+
description: "Send (or schedule with at, ISO time) an email campaign saved with wp_booking_save resource campaigns. Only customers who agreed to marketing emails get it, each with an unsubscribe link. Check the audience first: wp_booking_list resource campaigns id=<id>.",
|
|
38564
|
+
parameters: {
|
|
38565
|
+
type: "object",
|
|
38566
|
+
properties: {
|
|
38567
|
+
id: int2("Campaign id."),
|
|
38568
|
+
at: str2("Send at (ISO time); default now."),
|
|
38569
|
+
cancel: bool("Cancel a scheduled or running send instead.")
|
|
38570
|
+
},
|
|
38571
|
+
required: ["id"]
|
|
38572
|
+
},
|
|
38573
|
+
group: "site",
|
|
38574
|
+
need: WRITE,
|
|
38575
|
+
risk: "W3",
|
|
38576
|
+
gates: [...GATES],
|
|
38577
|
+
confirm: (a3, lang) => a3.cancel ? say(lang, `Stop campaign #${a3.id}.`, `\u505C\u6B62\u63A8\u5EE3\u6D3B\u52D5 #${a3.id}\u3002`) : say(
|
|
38578
|
+
lang,
|
|
38579
|
+
`Email campaign #${a3.id} to every customer in its audience who agreed to marketing emails${a3.at ? ` at ${a3.at}` : " now"}.`,
|
|
38580
|
+
`\u628A\u63A8\u5EE3\u6D3B\u52D5 #${a3.id}${a3.at ? `\u65BC ${a3.at}` : "\u7ACB\u5373"}\u5BC4\u7D66\u53D7\u773E\u4E2D\u540C\u610F\u63A5\u6536\u884C\u92B7\u96FB\u90F5\u7684\u5BA2\u6236\u3002`
|
|
38581
|
+
),
|
|
38582
|
+
run: (ctx, a3) => mb(ctx, "POST", `/campaigns/${Number(a3.id)}/${a3.cancel ? "cancel" : "send"}`, a3.cancel ? {} : { at: a3.at })
|
|
38583
|
+
}),
|
|
38584
|
+
tool({
|
|
38585
|
+
name: "wp_booking_membership",
|
|
38586
|
+
description: "Change a customer's membership (id from wp_booking_list resource memberships): pause, resume, cancel (at the end of the period), cancel_now, add_sessions (sessions), add_credit (amount_minor), mark_paid (a renewal paid at the desk) or charge (retry the renewal on the saved card).",
|
|
38587
|
+
parameters: {
|
|
38588
|
+
type: "object",
|
|
38589
|
+
properties: {
|
|
38590
|
+
id: int2("Membership id."),
|
|
38591
|
+
action: str2("pause | resume | cancel | cancel_now | add_sessions | add_credit | mark_paid | charge"),
|
|
38592
|
+
sessions: int2("For add_sessions."),
|
|
38593
|
+
amount_minor: int2("For add_credit."),
|
|
38594
|
+
note: str2("Note.")
|
|
38595
|
+
},
|
|
38596
|
+
required: ["id", "action"]
|
|
38597
|
+
},
|
|
38598
|
+
group: "site",
|
|
38599
|
+
need: WRITE,
|
|
38600
|
+
risk: "W3",
|
|
38601
|
+
gates: [...GATES],
|
|
38602
|
+
confirm: (a3, lang) => say(
|
|
38603
|
+
lang,
|
|
38604
|
+
`Membership #${a3.id}: ${a3.action}${a3.action === "charge" ? " (charges the saved card)" : ""}.`,
|
|
38605
|
+
`\u6703\u7C4D #${a3.id}\uFF1A${a3.action}${a3.action === "charge" ? "\uFF08\u5F9E\u5DF2\u5B58\u5361\u7247\u6263\u6B3E\uFF09" : ""}\u3002`
|
|
38606
|
+
),
|
|
38607
|
+
run: (ctx, a3) => mb(ctx, "POST", `/memberships/${Number(a3.id)}/${String(a3.action)}`, {
|
|
38608
|
+
sessions: a3.sessions,
|
|
38609
|
+
amount_minor: a3.amount_minor,
|
|
38610
|
+
note: a3.note
|
|
38611
|
+
})
|
|
38612
|
+
}),
|
|
38478
38613
|
tool({
|
|
38479
38614
|
name: "wp_booking_translate",
|
|
38480
38615
|
description: "Read or write the translations of booking content (type service | variant | category | staff | voucher, id, lang such as zh-hans or es; fields name, description). Without fields: read. The site's extra languages are set with wp_booking_settings_set settings.languages.",
|
|
@@ -41953,7 +42088,7 @@ var program = new Command();
|
|
|
41953
42088
|
// package.json
|
|
41954
42089
|
var package_default = {
|
|
41955
42090
|
name: "@cmo-so/cli",
|
|
41956
|
-
version: "0.12.
|
|
42091
|
+
version: "0.12.21",
|
|
41957
42092
|
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.",
|
|
41958
42093
|
type: "module",
|
|
41959
42094
|
license: "MIT",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cmo-so/cli",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.21",
|
|
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",
|