@cmo-so/cli 0.9.2 → 0.10.0
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 +687 -267
- package/package.json +1 -1
package/dist/cmo.mjs
CHANGED
|
@@ -449,9 +449,9 @@ var require_codegen = __commonJS({
|
|
|
449
449
|
}
|
|
450
450
|
};
|
|
451
451
|
var Label = class extends Node {
|
|
452
|
-
constructor(
|
|
452
|
+
constructor(label2) {
|
|
453
453
|
super();
|
|
454
|
-
this.label =
|
|
454
|
+
this.label = label2;
|
|
455
455
|
this.names = {};
|
|
456
456
|
}
|
|
457
457
|
render({ _n }) {
|
|
@@ -459,14 +459,14 @@ var require_codegen = __commonJS({
|
|
|
459
459
|
}
|
|
460
460
|
};
|
|
461
461
|
var Break = class extends Node {
|
|
462
|
-
constructor(
|
|
462
|
+
constructor(label2) {
|
|
463
463
|
super();
|
|
464
|
-
this.label =
|
|
464
|
+
this.label = label2;
|
|
465
465
|
this.names = {};
|
|
466
466
|
}
|
|
467
467
|
render({ _n }) {
|
|
468
|
-
const
|
|
469
|
-
return `break${
|
|
468
|
+
const label2 = this.label ? ` ${this.label}` : "";
|
|
469
|
+
return `break${label2};` + _n;
|
|
470
470
|
}
|
|
471
471
|
};
|
|
472
472
|
var Throw = class extends Node {
|
|
@@ -506,17 +506,17 @@ var require_codegen = __commonJS({
|
|
|
506
506
|
this.nodes = nodes;
|
|
507
507
|
}
|
|
508
508
|
render(opts) {
|
|
509
|
-
return this.nodes.reduce((code,
|
|
509
|
+
return this.nodes.reduce((code, n2) => code + n2.render(opts), "");
|
|
510
510
|
}
|
|
511
511
|
optimizeNodes() {
|
|
512
512
|
const { nodes } = this;
|
|
513
513
|
let i = nodes.length;
|
|
514
514
|
while (i--) {
|
|
515
|
-
const
|
|
516
|
-
if (Array.isArray(
|
|
517
|
-
nodes.splice(i, 1, ...
|
|
518
|
-
else if (
|
|
519
|
-
nodes[i] =
|
|
515
|
+
const n2 = nodes[i].optimizeNodes();
|
|
516
|
+
if (Array.isArray(n2))
|
|
517
|
+
nodes.splice(i, 1, ...n2);
|
|
518
|
+
else if (n2)
|
|
519
|
+
nodes[i] = n2;
|
|
520
520
|
else
|
|
521
521
|
nodes.splice(i, 1);
|
|
522
522
|
}
|
|
@@ -526,16 +526,16 @@ var require_codegen = __commonJS({
|
|
|
526
526
|
const { nodes } = this;
|
|
527
527
|
let i = nodes.length;
|
|
528
528
|
while (i--) {
|
|
529
|
-
const
|
|
530
|
-
if (
|
|
529
|
+
const n2 = nodes[i];
|
|
530
|
+
if (n2.optimizeNames(names, constants))
|
|
531
531
|
continue;
|
|
532
|
-
subtractNames(names,
|
|
532
|
+
subtractNames(names, n2.names);
|
|
533
533
|
nodes.splice(i, 1);
|
|
534
534
|
}
|
|
535
535
|
return nodes.length > 0 ? this : void 0;
|
|
536
536
|
}
|
|
537
537
|
get names() {
|
|
538
|
-
return this.nodes.reduce((names,
|
|
538
|
+
return this.nodes.reduce((names, n2) => addNames(names, n2.names), {});
|
|
539
539
|
}
|
|
540
540
|
};
|
|
541
541
|
var BlockNode = class extends ParentNode {
|
|
@@ -878,12 +878,12 @@ var require_codegen = __commonJS({
|
|
|
878
878
|
return this._endBlockNode(For);
|
|
879
879
|
}
|
|
880
880
|
// `label` statement
|
|
881
|
-
label(
|
|
882
|
-
return this._leafNode(new Label(
|
|
881
|
+
label(label2) {
|
|
882
|
+
return this._leafNode(new Label(label2));
|
|
883
883
|
}
|
|
884
884
|
// `break` statement
|
|
885
|
-
break(
|
|
886
|
-
return this._leafNode(new Break(
|
|
885
|
+
break(label2) {
|
|
886
|
+
return this._leafNode(new Break(label2));
|
|
887
887
|
}
|
|
888
888
|
// `return` statement
|
|
889
889
|
return(value) {
|
|
@@ -946,8 +946,8 @@ var require_codegen = __commonJS({
|
|
|
946
946
|
endFunc() {
|
|
947
947
|
return this._endBlockNode(Func);
|
|
948
948
|
}
|
|
949
|
-
optimize(
|
|
950
|
-
while (
|
|
949
|
+
optimize(n2 = 1) {
|
|
950
|
+
while (n2-- > 0) {
|
|
951
951
|
this._root.optimizeNodes();
|
|
952
952
|
this._root.optimizeNames(this._root.names, this._constants);
|
|
953
953
|
}
|
|
@@ -961,19 +961,19 @@ var require_codegen = __commonJS({
|
|
|
961
961
|
this._nodes.push(node2);
|
|
962
962
|
}
|
|
963
963
|
_endBlockNode(N1, N2) {
|
|
964
|
-
const
|
|
965
|
-
if (
|
|
964
|
+
const n2 = this._currNode;
|
|
965
|
+
if (n2 instanceof N1 || N2 && n2 instanceof N2) {
|
|
966
966
|
this._nodes.pop();
|
|
967
967
|
return this;
|
|
968
968
|
}
|
|
969
969
|
throw new Error(`CodeGen: not in block "${N2 ? `${N1.kind}/${N2.kind}` : N1.kind}"`);
|
|
970
970
|
}
|
|
971
971
|
_elseNode(node2) {
|
|
972
|
-
const
|
|
973
|
-
if (!(
|
|
972
|
+
const n2 = this._currNode;
|
|
973
|
+
if (!(n2 instanceof If)) {
|
|
974
974
|
throw new Error('CodeGen: "else" without "if"');
|
|
975
975
|
}
|
|
976
|
-
this._currNode =
|
|
976
|
+
this._currNode = n2.else = node2;
|
|
977
977
|
return this;
|
|
978
978
|
}
|
|
979
979
|
get _root() {
|
|
@@ -990,8 +990,8 @@ var require_codegen = __commonJS({
|
|
|
990
990
|
};
|
|
991
991
|
exports.CodeGen = CodeGen;
|
|
992
992
|
function addNames(names, from) {
|
|
993
|
-
for (const
|
|
994
|
-
names[
|
|
993
|
+
for (const n2 in from)
|
|
994
|
+
names[n2] = (names[n2] || 0) + (from[n2] || 0);
|
|
995
995
|
return names;
|
|
996
996
|
}
|
|
997
997
|
function addExprNames(names, from) {
|
|
@@ -1002,20 +1002,20 @@ var require_codegen = __commonJS({
|
|
|
1002
1002
|
return replaceName(expr);
|
|
1003
1003
|
if (!canOptimize(expr))
|
|
1004
1004
|
return expr;
|
|
1005
|
-
return new code_1._Code(expr._items.reduce((
|
|
1005
|
+
return new code_1._Code(expr._items.reduce((items2, c) => {
|
|
1006
1006
|
if (c instanceof code_1.Name)
|
|
1007
1007
|
c = replaceName(c);
|
|
1008
1008
|
if (c instanceof code_1._Code)
|
|
1009
|
-
|
|
1009
|
+
items2.push(...c._items);
|
|
1010
1010
|
else
|
|
1011
|
-
|
|
1012
|
-
return
|
|
1011
|
+
items2.push(c);
|
|
1012
|
+
return items2;
|
|
1013
1013
|
}, []));
|
|
1014
|
-
function replaceName(
|
|
1015
|
-
const c = constants[
|
|
1016
|
-
if (c === void 0 || names[
|
|
1017
|
-
return
|
|
1018
|
-
delete names[
|
|
1014
|
+
function replaceName(n2) {
|
|
1015
|
+
const c = constants[n2.str];
|
|
1016
|
+
if (c === void 0 || names[n2.str] !== 1)
|
|
1017
|
+
return n2;
|
|
1018
|
+
delete names[n2.str];
|
|
1019
1019
|
return c;
|
|
1020
1020
|
}
|
|
1021
1021
|
function canOptimize(e) {
|
|
@@ -1023,8 +1023,8 @@ var require_codegen = __commonJS({
|
|
|
1023
1023
|
}
|
|
1024
1024
|
}
|
|
1025
1025
|
function subtractNames(names, from) {
|
|
1026
|
-
for (const
|
|
1027
|
-
names[
|
|
1026
|
+
for (const n2 in from)
|
|
1027
|
+
names[n2] = (names[n2] || 0) - (from[n2] || 0);
|
|
1028
1028
|
}
|
|
1029
1029
|
function not(x) {
|
|
1030
1030
|
return typeof x == "boolean" || typeof x == "number" || x === null ? !x : (0, code_1._)`!${par(x)}`;
|
|
@@ -1167,7 +1167,7 @@ var require_util = __commonJS({
|
|
|
1167
1167
|
mergeNames: (gen, from, to) => gen.if((0, codegen_1._)`${to} !== true && ${from} !== undefined`, () => gen.assign(to, (0, codegen_1._)`${from} === true ? true : ${to} > ${from} ? ${to} : ${from}`)),
|
|
1168
1168
|
mergeToName: (gen, from, to) => gen.if((0, codegen_1._)`${to} !== true`, () => gen.assign(to, from === true ? true : (0, codegen_1._)`${to} > ${from} ? ${to} : ${from}`)),
|
|
1169
1169
|
mergeValues: (from, to) => from === true ? true : Math.max(from, to),
|
|
1170
|
-
resultToName: (gen,
|
|
1170
|
+
resultToName: (gen, items2) => gen.var("items", items2)
|
|
1171
1171
|
})
|
|
1172
1172
|
};
|
|
1173
1173
|
function evaluatedPropsToName(gen, ps) {
|
|
@@ -1361,12 +1361,12 @@ var require_errors = __commonJS({
|
|
|
1361
1361
|
}
|
|
1362
1362
|
return [E.schemaPath, schPath];
|
|
1363
1363
|
}
|
|
1364
|
-
function extraErrorProps(cxt, { params, message }, keyValues) {
|
|
1364
|
+
function extraErrorProps(cxt, { params, message: message2 }, keyValues) {
|
|
1365
1365
|
const { keyword, data, schemaValue, it } = cxt;
|
|
1366
1366
|
const { opts, propertyName, topSchemaRef, schemaPath } = it;
|
|
1367
1367
|
keyValues.push([E.keyword, keyword], [E.params, typeof params == "function" ? params(cxt) : params || (0, codegen_1._)`{}`]);
|
|
1368
1368
|
if (opts.messages) {
|
|
1369
|
-
keyValues.push([E.message, typeof
|
|
1369
|
+
keyValues.push([E.message, typeof message2 == "function" ? message2(cxt) : message2]);
|
|
1370
1370
|
}
|
|
1371
1371
|
if (opts.verbose) {
|
|
1372
1372
|
keyValues.push([E.schema, schemaValue], [E.parentSchema, (0, codegen_1._)`${topSchemaRef}${schemaPath}`], [names_1.default.data, data]);
|
|
@@ -1675,13 +1675,13 @@ var require_defaults = __commonJS({
|
|
|
1675
1675
|
var codegen_1 = require_codegen();
|
|
1676
1676
|
var util_1 = require_util();
|
|
1677
1677
|
function assignDefaults(it, ty) {
|
|
1678
|
-
const { properties, items } = it.schema;
|
|
1678
|
+
const { properties, items: items2 } = it.schema;
|
|
1679
1679
|
if (ty === "object" && properties) {
|
|
1680
1680
|
for (const key in properties) {
|
|
1681
1681
|
assignDefault(it, key, properties[key].default);
|
|
1682
1682
|
}
|
|
1683
|
-
} else if (ty === "array" && Array.isArray(
|
|
1684
|
-
|
|
1683
|
+
} else if (ty === "array" && Array.isArray(items2)) {
|
|
1684
|
+
items2.forEach((sch, i) => assignDefault(it, i, sch.default));
|
|
1685
1685
|
}
|
|
1686
1686
|
}
|
|
1687
1687
|
exports.assignDefaults = assignDefaults;
|
|
@@ -2485,11 +2485,11 @@ var require_validate = __commonJS({
|
|
|
2485
2485
|
gen.return((0, codegen_1._)`${names_1.default.errors} === 0`);
|
|
2486
2486
|
}
|
|
2487
2487
|
}
|
|
2488
|
-
function assignEvaluated({ gen, evaluated, props, items }) {
|
|
2488
|
+
function assignEvaluated({ gen, evaluated, props, items: items2 }) {
|
|
2489
2489
|
if (props instanceof codegen_1.Name)
|
|
2490
2490
|
gen.assign((0, codegen_1._)`${evaluated}.props`, props);
|
|
2491
|
-
if (
|
|
2492
|
-
gen.assign((0, codegen_1._)`${evaluated}.items`,
|
|
2491
|
+
if (items2 instanceof codegen_1.Name)
|
|
2492
|
+
gen.assign((0, codegen_1._)`${evaluated}.items`, items2);
|
|
2493
2493
|
}
|
|
2494
2494
|
function schemaKeywords(it, types, typeErrors, errsCount) {
|
|
2495
2495
|
const { gen, schema, data, allErrors, opts, self } = it;
|
|
@@ -2952,12 +2952,12 @@ var require_compile = __commonJS({
|
|
|
2952
2952
|
validate2.source = { validateName, validateCode, scopeValues: gen._values };
|
|
2953
2953
|
}
|
|
2954
2954
|
if (this.opts.unevaluated) {
|
|
2955
|
-
const { props, items } = schemaCxt;
|
|
2955
|
+
const { props, items: items2 } = schemaCxt;
|
|
2956
2956
|
validate2.evaluated = {
|
|
2957
2957
|
props: props instanceof codegen_1.Name ? void 0 : props,
|
|
2958
|
-
items:
|
|
2958
|
+
items: items2 instanceof codegen_1.Name ? void 0 : items2,
|
|
2959
2959
|
dynamicProps: props instanceof codegen_1.Name,
|
|
2960
|
-
dynamicItems:
|
|
2960
|
+
dynamicItems: items2 instanceof codegen_1.Name
|
|
2961
2961
|
};
|
|
2962
2962
|
if (validate2.source)
|
|
2963
2963
|
validate2.source.evaluated = (0, codegen_1.stringify)(validate2.evaluated);
|
|
@@ -4496,11 +4496,11 @@ var require_core = __commonJS({
|
|
|
4496
4496
|
}
|
|
4497
4497
|
const valid = this.validate($schema, schema);
|
|
4498
4498
|
if (!valid && throwOrLogError) {
|
|
4499
|
-
const
|
|
4499
|
+
const message2 = "schema is invalid: " + this.errorsText();
|
|
4500
4500
|
if (this.opts.validateSchema === "log")
|
|
4501
|
-
this.logger.error(
|
|
4501
|
+
this.logger.error(message2);
|
|
4502
4502
|
else
|
|
4503
|
-
throw new Error(
|
|
4503
|
+
throw new Error(message2);
|
|
4504
4504
|
}
|
|
4505
4505
|
return valid;
|
|
4506
4506
|
}
|
|
@@ -4967,8 +4967,8 @@ var require_ref = __commonJS({
|
|
|
4967
4967
|
it.items = util_1.mergeEvaluated.items(gen, schEvaluated.items, it.items);
|
|
4968
4968
|
}
|
|
4969
4969
|
} else {
|
|
4970
|
-
const
|
|
4971
|
-
it.items = util_1.mergeEvaluated.items(gen,
|
|
4970
|
+
const items2 = gen.var("items", (0, codegen_1._)`${source}.evaluated.items`);
|
|
4971
|
+
it.items = util_1.mergeEvaluated.items(gen, items2, it.items, codegen_1.Name);
|
|
4972
4972
|
}
|
|
4973
4973
|
}
|
|
4974
4974
|
}
|
|
@@ -5508,28 +5508,28 @@ var require_additionalItems = __commonJS({
|
|
|
5508
5508
|
error: error2,
|
|
5509
5509
|
code(cxt) {
|
|
5510
5510
|
const { parentSchema, it } = cxt;
|
|
5511
|
-
const { items } = parentSchema;
|
|
5512
|
-
if (!Array.isArray(
|
|
5511
|
+
const { items: items2 } = parentSchema;
|
|
5512
|
+
if (!Array.isArray(items2)) {
|
|
5513
5513
|
(0, util_1.checkStrictMode)(it, '"additionalItems" is ignored when "items" is not an array of schemas');
|
|
5514
5514
|
return;
|
|
5515
5515
|
}
|
|
5516
|
-
validateAdditionalItems(cxt,
|
|
5516
|
+
validateAdditionalItems(cxt, items2);
|
|
5517
5517
|
}
|
|
5518
5518
|
};
|
|
5519
|
-
function validateAdditionalItems(cxt,
|
|
5519
|
+
function validateAdditionalItems(cxt, items2) {
|
|
5520
5520
|
const { gen, schema, data, keyword, it } = cxt;
|
|
5521
5521
|
it.items = true;
|
|
5522
5522
|
const len = gen.const("len", (0, codegen_1._)`${data}.length`);
|
|
5523
5523
|
if (schema === false) {
|
|
5524
|
-
cxt.setParams({ len:
|
|
5525
|
-
cxt.pass((0, codegen_1._)`${len} <= ${
|
|
5524
|
+
cxt.setParams({ len: items2.length });
|
|
5525
|
+
cxt.pass((0, codegen_1._)`${len} <= ${items2.length}`);
|
|
5526
5526
|
} else if (typeof schema == "object" && !(0, util_1.alwaysValidSchema)(it, schema)) {
|
|
5527
|
-
const valid = gen.var("valid", (0, codegen_1._)`${len} <= ${
|
|
5527
|
+
const valid = gen.var("valid", (0, codegen_1._)`${len} <= ${items2.length}`);
|
|
5528
5528
|
gen.if((0, codegen_1.not)(valid), () => validateItems(valid));
|
|
5529
5529
|
cxt.ok(valid);
|
|
5530
5530
|
}
|
|
5531
5531
|
function validateItems(valid) {
|
|
5532
|
-
gen.forRange("i",
|
|
5532
|
+
gen.forRange("i", items2.length, len, (i) => {
|
|
5533
5533
|
cxt.subschema({ keyword, dataProp: i, dataPropType: util_1.Type.Num }, valid);
|
|
5534
5534
|
if (!it.allErrors)
|
|
5535
5535
|
gen.if((0, codegen_1.not)(valid), () => gen.break());
|
|
@@ -7211,14 +7211,14 @@ var OAuthError = class extends Error {
|
|
|
7211
7211
|
}
|
|
7212
7212
|
};
|
|
7213
7213
|
var SignInRequiredError = class extends Error {
|
|
7214
|
-
constructor(
|
|
7215
|
-
super(
|
|
7214
|
+
constructor(message2 = "Please sign in again.") {
|
|
7215
|
+
super(message2);
|
|
7216
7216
|
this.name = "SignInRequiredError";
|
|
7217
7217
|
}
|
|
7218
7218
|
};
|
|
7219
7219
|
var ApiError = class extends Error {
|
|
7220
|
-
constructor(status,
|
|
7221
|
-
super(
|
|
7220
|
+
constructor(status, message2, requestId, detail = null) {
|
|
7221
|
+
super(message2);
|
|
7222
7222
|
this.status = status;
|
|
7223
7223
|
this.requestId = requestId;
|
|
7224
7224
|
this.detail = detail;
|
|
@@ -7448,10 +7448,10 @@ function createApi({ baseUrl, session: session2, fetch: fetchImpl = globalThis.f
|
|
|
7448
7448
|
}),
|
|
7449
7449
|
models: () => request("/v1/maggie/bridge/models"),
|
|
7450
7450
|
/** Add a ChatGPT account to the bridge (then connect it). */
|
|
7451
|
-
addAccount: (
|
|
7451
|
+
addAccount: (label2) => request("/v1/maggie/bridge/accounts", {
|
|
7452
7452
|
method: "POST",
|
|
7453
7453
|
headers: { "Content-Type": "application/json" },
|
|
7454
|
-
body: JSON.stringify({ label:
|
|
7454
|
+
body: JSON.stringify({ label: label2 ?? null })
|
|
7455
7455
|
}),
|
|
7456
7456
|
/** Make one of the bridge's ChatGPT accounts the one that answers. */
|
|
7457
7457
|
chooseAccount: (id) => request(`/v1/maggie/bridge/accounts/${encodeURIComponent(id)}/use`, { method: "POST" }),
|
|
@@ -7470,8 +7470,8 @@ function createApi({ baseUrl, session: session2, fetch: fetchImpl = globalThis.f
|
|
|
7470
7470
|
/** The user has read the team's latest reply or status change. */
|
|
7471
7471
|
seen: (id) => request(`/v1/feedback/${encodeURIComponent(id)}/seen`, { method: "POST" }),
|
|
7472
7472
|
/** One of the user's feedback photos as a data: URL (an <Image> source on web and native). */
|
|
7473
|
-
photo: async (id,
|
|
7474
|
-
const { type, bytes } = await requestBytes(`/v1/feedback/${encodeURIComponent(id)}/photos/${
|
|
7473
|
+
photo: async (id, n2) => {
|
|
7474
|
+
const { type, bytes } = await requestBytes(`/v1/feedback/${encodeURIComponent(id)}/photos/${n2}`);
|
|
7475
7475
|
return dataUrl(type, bytes);
|
|
7476
7476
|
}
|
|
7477
7477
|
},
|
|
@@ -7521,8 +7521,8 @@ function errorMessage(data, status) {
|
|
|
7521
7521
|
const detail = data?.detail;
|
|
7522
7522
|
if (typeof detail === "string" && detail) return detail;
|
|
7523
7523
|
if (detail && typeof detail === "object" && !Array.isArray(detail)) {
|
|
7524
|
-
const
|
|
7525
|
-
if (typeof
|
|
7524
|
+
const message2 = detail.message;
|
|
7525
|
+
if (typeof message2 === "string" && message2) return message2;
|
|
7526
7526
|
}
|
|
7527
7527
|
if (Array.isArray(detail)) {
|
|
7528
7528
|
const first = detail[0];
|
|
@@ -7540,8 +7540,8 @@ function dataUrl(type, bytes) {
|
|
|
7540
7540
|
const parts = [];
|
|
7541
7541
|
let chunk = "";
|
|
7542
7542
|
for (let i = 0; i < b.length; i += 3) {
|
|
7543
|
-
const
|
|
7544
|
-
chunk += B64.charAt(
|
|
7543
|
+
const n2 = (b[i] ?? 0) << 16 | (b[i + 1] ?? 0) << 8 | (b[i + 2] ?? 0);
|
|
7544
|
+
chunk += B64.charAt(n2 >> 18 & 63) + B64.charAt(n2 >> 12 & 63) + (i + 1 < b.length ? B64.charAt(n2 >> 6 & 63) : "=") + (i + 2 < b.length ? B64.charAt(n2 & 63) : "=");
|
|
7545
7545
|
if (chunk.length >= 8192) {
|
|
7546
7546
|
parts.push(chunk);
|
|
7547
7547
|
chunk = "";
|
|
@@ -7921,8 +7921,9 @@ function domainApi(api2, domain) {
|
|
|
7921
7921
|
* https links to pictures. Send a few photos per call (16 MB request limit), `append` after the first.
|
|
7922
7922
|
* On LinkedIn a set goes out as a swipeable PDF carousel unless setSocialCarousel says images. */
|
|
7923
7923
|
setSocialPictures: (id, pictures) => post(`${base}/social/${id}/pictures`, pictures),
|
|
7924
|
-
/** A
|
|
7925
|
-
*
|
|
7924
|
+
/** A PDF carousel (base64 under ~11 MB, or a public https link; up to 300 pages): its first 20 pages become
|
|
7925
|
+
* the post's pictures; a LinkedIn post (or the idea's LinkedIn post) goes out as the PDF itself. LinkedIn,
|
|
7926
|
+
* Instagram, Facebook and TikTok posts. */
|
|
7926
7927
|
attachSocialCarouselPdf: (id, pdf) => post(`${base}/social/${id}/carousel/pdf`, pdf),
|
|
7927
7928
|
/** How a LinkedIn post with a set goes out: "pdf" (swipeable carousel, the default) or "images"; its title. */
|
|
7928
7929
|
setSocialCarousel: (id, changes) => api2.request(`${base}/social/${id}/carousel`, {
|
|
@@ -8072,6 +8073,26 @@ function domainApi(api2, domain) {
|
|
|
8072
8073
|
/** A post on one video channel (YouTube by default, or TikTok) from a ready video, waiting in Review; Maggie
|
|
8073
8074
|
* writes the details (YouTube) or the caption (TikTok) unless write is false. */
|
|
8074
8075
|
postFromVideo: (videoId, opts = {}) => post(`${base}/social/video/${videoId}/post`, opts),
|
|
8076
|
+
/** A TikTok photo post (a swipeable set; TikTok adds music) from this post's pictures, waiting in Review;
|
|
8077
|
+
* Maggie writes the caption unless `caption` is given or write is false. Beta, like TikTok. */
|
|
8078
|
+
tiktokPhotoPost: (id, opts = {}) => post(`${base}/social/${id}/tiktok-photo`, opts),
|
|
8079
|
+
/** Ad videos (Beta): this website's, with the voices and lengths to choose from. */
|
|
8080
|
+
adVideos: () => api2.request(
|
|
8081
|
+
`${base}/social/ad-videos`
|
|
8082
|
+
),
|
|
8083
|
+
adVideo: (adId) => api2.request(`${base}/social/ad-videos/${adId}`),
|
|
8084
|
+
/** A storyboard and its cost (one AI generation; nothing billed by the second yet). */
|
|
8085
|
+
planAdVideo: (opts) => post(`${base}/social/ad-videos`, opts),
|
|
8086
|
+
/** Change the storyboard: only what a change touches is made again (`redo`), with the new cost. */
|
|
8087
|
+
changeAdVideo: (adId, changes) => api2.request(`${base}/social/ad-videos/${adId}`, {
|
|
8088
|
+
method: "PATCH",
|
|
8089
|
+
headers: { "Content-Type": "application/json" },
|
|
8090
|
+
body: JSON.stringify(changes)
|
|
8091
|
+
}),
|
|
8092
|
+
/** Make it (billed): send back the estimate the person agreed to. */
|
|
8093
|
+
makeAdVideo: (adId, estimateUsd) => post(`${base}/social/ad-videos/${adId}/make`, { confirm: true, estimate_usd: estimateUsd }),
|
|
8094
|
+
/** Stop one being made (what's made is kept), or remove one. */
|
|
8095
|
+
stopAdVideo: (adId) => api2.request(`${base}/social/ad-videos/${adId}`, { method: "DELETE" }),
|
|
8075
8096
|
/** Make a LinkedIn, Facebook or Instagram post a video post: it goes out as this video (its picture is dropped). */
|
|
8076
8097
|
attachVideo: (id, videoId) => post(`${base}/social/${id}/video`, { video_id: videoId }),
|
|
8077
8098
|
/** Take the video off a post: it's a text post again. */
|
|
@@ -8409,8 +8430,8 @@ function cleanSitemap(entry, domain) {
|
|
|
8409
8430
|
if (!url) return "The sitemap must be a web address starting with http:// or https://.";
|
|
8410
8431
|
let owner = SITEMAP_OWNERS.includes(entry.owner) ? entry.owner : "other";
|
|
8411
8432
|
if (owner === "own" && domain && !onWebsite(url, domain)) owner = "other";
|
|
8412
|
-
const
|
|
8413
|
-
const out = { ...entry, url, owner, label:
|
|
8433
|
+
const label2 = (entry.label ?? "").trim().slice(0, SITEMAP_LABEL_MAX);
|
|
8434
|
+
const out = { ...entry, url, owner, label: label2 || null, uses: sitemapUses(owner, entry.uses) };
|
|
8414
8435
|
if (!out.id) delete out.id;
|
|
8415
8436
|
return out;
|
|
8416
8437
|
}
|
|
@@ -8427,8 +8448,8 @@ function checkSitemaps(list2) {
|
|
|
8427
8448
|
function sitemapCheckLine(check) {
|
|
8428
8449
|
if (!check || check.status === "unchecked") return "Checked when you save";
|
|
8429
8450
|
if (check.status === "error") return check.error?.trim() || "Couldn't read this sitemap.";
|
|
8430
|
-
const
|
|
8431
|
-
const found =
|
|
8451
|
+
const n2 = check.url_count;
|
|
8452
|
+
const found = n2 == null ? "Read" : `Found ${n2.toLocaleString("en-GB")} ${n2 === 1 ? "page" : "pages"}`;
|
|
8432
8453
|
const at = check.checked_at ? new Date(check.checked_at) : null;
|
|
8433
8454
|
const when = at && !Number.isNaN(at.getTime()) ? at.toLocaleDateString("en-GB", { day: "numeric", month: "short", year: "numeric" }) : null;
|
|
8434
8455
|
return when ? `${found} \xB7 checked ${when}` : found;
|
|
@@ -8456,13 +8477,13 @@ var named = (title, id) => {
|
|
|
8456
8477
|
const s = String(id ?? "").trim();
|
|
8457
8478
|
return s && s.length <= 8 ? ` #${s}` : "";
|
|
8458
8479
|
};
|
|
8459
|
-
var list = (xs,
|
|
8480
|
+
var list = (xs, n2 = 5) => {
|
|
8460
8481
|
const a = (Array.isArray(xs) ? xs : xs == null ? [] : [xs]).map(String);
|
|
8461
|
-
return a.length >
|
|
8482
|
+
return a.length > n2 ? `${a.slice(0, n2).join(", ")} +${a.length - n2}` : a.join(", ");
|
|
8462
8483
|
};
|
|
8463
|
-
var placeEn = (p,
|
|
8484
|
+
var placeEn = (p, items2 = "posts") => {
|
|
8464
8485
|
const t = String(p ?? "").trim().toLowerCase();
|
|
8465
|
-
if (t === "end") return `at the end of the queue, after the ${
|
|
8486
|
+
if (t === "end") return `at the end of the queue, after the ${items2} already waiting`;
|
|
8466
8487
|
if (t === "random") return "at a random place in the queue";
|
|
8467
8488
|
if (/^\d+$/.test(t)) return Number(t) === 0 ? null : `after the first ${t} in the queue`;
|
|
8468
8489
|
return null;
|
|
@@ -8479,6 +8500,12 @@ var keys = (a) => Object.keys(a ?? {}).filter((k) => a[k] !== void 0);
|
|
|
8479
8500
|
var videoChannels = (a) => (Array.isArray(a.channels) && a.channels.length ? a.channels : ["youtube"]).map(
|
|
8480
8501
|
(c) => ({ tiktok: "TikTok", linkedin: "LinkedIn", facebook: "Facebook", instagram: "Instagram" })[String(c)] ?? "YouTube"
|
|
8481
8502
|
);
|
|
8503
|
+
var videoFrom = (a, lang = "en") => {
|
|
8504
|
+
const name = a.file?.file_name;
|
|
8505
|
+
if (a.file) return name ? q(String(name)) : lang === "en" ? "your video" : "\u4F60\u7684\u5F71\u7247";
|
|
8506
|
+
if (a.video_id) return lang === "en" ? `video #${a.video_id}` : `\u5F71\u7247 #${a.video_id}`;
|
|
8507
|
+
return String(a.url ?? "");
|
|
8508
|
+
};
|
|
8482
8509
|
var confirmCopy = {
|
|
8483
8510
|
// --------------------------------------------------------- account / setup
|
|
8484
8511
|
website_add: {
|
|
@@ -8553,13 +8580,13 @@ var confirmCopy = {
|
|
|
8553
8580
|
sitemap_upsert: {
|
|
8554
8581
|
en: (a) => {
|
|
8555
8582
|
const whose = { own: "your site's ", competitor: "a competitor's ", other: "another site's " }[a.owner];
|
|
8556
|
-
const
|
|
8557
|
-
return a.url ? `Save ${whose ?? "the "}sitemap ${a.url}${
|
|
8583
|
+
const label2 = a.label ? ` (${q(a.label)})` : "";
|
|
8584
|
+
return a.url ? `Save ${whose ?? "the "}sitemap ${a.url}${label2}.` : `Change the sitemap${label2}: ${keys(a).filter((k) => k !== "id").join(", ")}.`;
|
|
8558
8585
|
},
|
|
8559
8586
|
"zh-Hant": (a) => {
|
|
8560
8587
|
const whose = { own: "\u81EA\u5DF1\u7DB2\u7AD9\u7684", competitor: "\u7AF6\u722D\u5C0D\u624B\u7684", other: "\u5176\u4ED6\u7DB2\u7AD9\u7684" }[a.owner];
|
|
8561
|
-
const
|
|
8562
|
-
return a.url ? `\u5132\u5B58${whose ?? ""} sitemap ${a.url}${
|
|
8588
|
+
const label2 = a.label ? `\uFF08${q(a.label)}\uFF09` : "";
|
|
8589
|
+
return a.url ? `\u5132\u5B58${whose ?? ""} sitemap ${a.url}${label2}\u3002` : `\u4FEE\u6539 sitemap${label2}\uFF1A${keys(a).filter((k) => k !== "id").join("\u3001")}\u3002`;
|
|
8563
8590
|
}
|
|
8564
8591
|
},
|
|
8565
8592
|
sitemap_remove: {
|
|
@@ -8717,18 +8744,18 @@ var confirmCopy = {
|
|
|
8717
8744
|
},
|
|
8718
8745
|
social_approve: {
|
|
8719
8746
|
en: (a) => {
|
|
8720
|
-
const
|
|
8747
|
+
const n2 = ids(a);
|
|
8721
8748
|
const at = placeEn(a.position);
|
|
8722
8749
|
if (at)
|
|
8723
|
-
return `Approve ${
|
|
8724
|
-
return
|
|
8750
|
+
return `Approve ${n2.length > 1 ? `${n2.length} posts: ${n2.map((i) => `#${i}`).join(", ")}` : `post #${n2[0]}`}. ${n2.length > 1 ? "They are" : "It is"} queued ${at}, and go${n2.length > 1 ? "" : "es"} out where your network can see ${n2.length > 1 ? "them" : "it"}.`;
|
|
8751
|
+
return n2.length > 1 ? `Approve ${n2.length} posts: ${n2.map((i) => `#${i}`).join(", ")}. Each goes out at your next posting times, where your network can see it.` : `Approve post #${n2[0]}. It goes out at your next posting time, where your network can see it.`;
|
|
8725
8752
|
},
|
|
8726
8753
|
"zh-Hant": (a) => {
|
|
8727
|
-
const
|
|
8754
|
+
const n2 = ids(a);
|
|
8728
8755
|
const at = placeZh(a.position);
|
|
8729
8756
|
if (at)
|
|
8730
|
-
return `\u6279\u51C6${
|
|
8731
|
-
return
|
|
8757
|
+
return `\u6279\u51C6${n2.length > 1 ? ` ${n2.length} \u7BC7\u8CBC\u6587\uFF1A${n2.map((i) => `#${i}`).join("\u3001")}` : `\u8CBC\u6587 #${n2[0]}`}\u3002${at}\uFF0C\u767C\u51FA\u5F8C\u4F60\u7684\u4EBA\u8108\u90FD\u770B\u5F97\u5230\u3002`;
|
|
8758
|
+
return n2.length > 1 ? `\u6279\u51C6 ${n2.length} \u7BC7\u8CBC\u6587\uFF1A${n2.map((i) => `#${i}`).join("\u3001")}\u3002\u6BCF\u7BC7\u5728\u63A5\u4E0B\u4F86\u7684\u767C\u6587\u6642\u9593\u767C\u51FA\uFF0C\u4F60\u7684\u4EBA\u8108\u90FD\u770B\u5F97\u5230\u3002` : `\u6279\u51C6\u8CBC\u6587 #${n2[0]}\u3002\u5B83\u6703\u5728\u4E0B\u4E00\u500B\u767C\u6587\u6642\u9593\u767C\u51FA\uFF0C\u4F60\u7684\u4EBA\u8108\u90FD\u770B\u5F97\u5230\u3002`;
|
|
8732
8759
|
}
|
|
8733
8760
|
},
|
|
8734
8761
|
social_publish_now: {
|
|
@@ -8753,8 +8780,8 @@ var confirmCopy = {
|
|
|
8753
8780
|
"zh-Hant": () => "\u7522\u751F\u9023\u63A5\u4F60 YouTube \u983B\u9053\u7684\u9023\u7D50\u3002"
|
|
8754
8781
|
},
|
|
8755
8782
|
social_video_attach: {
|
|
8756
|
-
en: (a) => `Make post #${a.id} a video post with ${a
|
|
8757
|
-
"zh-Hant": (a) => `\u628A\u8CBC\u6587 #${a.id} \u6539\u6210\u5F71\u7247\u8CBC\u6587\uFF0C\u7528 ${a
|
|
8783
|
+
en: (a) => `Make post #${a.id} a video post with ${videoFrom(a)} (its picture is removed).`,
|
|
8784
|
+
"zh-Hant": (a) => `\u628A\u8CBC\u6587 #${a.id} \u6539\u6210\u5F71\u7247\u8CBC\u6587\uFF0C\u7528 ${videoFrom(a, "zh-Hant")}\uFF08\u5716\u7247\u6703\u79FB\u9664\uFF09\u3002`
|
|
8758
8785
|
},
|
|
8759
8786
|
social_video_detach: {
|
|
8760
8787
|
en: (a) => `Take the video off post #${a.id} (it's a text post again).`,
|
|
@@ -8767,11 +8794,11 @@ var confirmCopy = {
|
|
|
8767
8794
|
social_video_add: {
|
|
8768
8795
|
en: (a) => {
|
|
8769
8796
|
const names = videoChannels(a).join(" and ");
|
|
8770
|
-
return `Make a ${names} post from ${a
|
|
8797
|
+
return `Make a ${names} post from ${videoFrom(a)}${a.write === false ? "" : ` (Maggie writes it: one AI generation per channel)`}; it waits for your OK.`;
|
|
8771
8798
|
},
|
|
8772
8799
|
"zh-Hant": (a) => {
|
|
8773
8800
|
const names = videoChannels(a).join(" \u548C ");
|
|
8774
|
-
return `\u7528 ${a
|
|
8801
|
+
return `\u7528 ${videoFrom(a, "zh-Hant")} \u505A ${names} \u5F71\u7247\u8CBC\u6587${a.write === false ? "" : "\uFF08Maggie \u4F86\u5BEB\uFF1A\u6BCF\u500B\u6E20\u9053\u7528\u4E00\u6B21 AI \u751F\u6210\uFF09"}\uFF0C\u7B49\u4F60\u78BA\u8A8D\u5F8C\u624D\u767C\u3002`;
|
|
8775
8802
|
}
|
|
8776
8803
|
},
|
|
8777
8804
|
social_tiktok_set: {
|
|
@@ -8787,8 +8814,8 @@ var confirmCopy = {
|
|
|
8787
8814
|
"zh-Hant": (a) => `\u4FEE\u6539 YouTube \u9810\u8A2D\uFF1A${keys(a).join("\u3001") || "\u7121"}\u3002`
|
|
8788
8815
|
},
|
|
8789
8816
|
social_image_new: {
|
|
8790
|
-
en: (a) => `Make a new picture for post #${a.id}${a.instruction ? `: ${String(a.instruction).slice(0, 300)}` : ""} (1 AI generation, in the background)${a.replace ? ", stopping the one being made for another post" : ""}.`,
|
|
8791
|
-
"zh-Hant": (a) => `\u70BA\u8CBC\u6587 #${a.id} \u751F\u6210\u65B0\u5716\u7247${a.instruction ? `\uFF1A${String(a.instruction).slice(0, 300)}` : ""}\uFF08\u7528 1 \u6B21 AI \u751F\u6210\uFF0C\u5728\u80CC\u666F\u9032\u884C\uFF09${a.replace ? "\uFF0C\u4E26\u505C\u6B62\u6B63\u5728\u70BA\u53E6\u4E00\u7BC7\u751F\u6210\u7684\u5716\u7247" : ""}\u3002`
|
|
8817
|
+
en: (a) => Number(a.count) >= 5 ? `Make a text carousel of ${a.count} slides for post #${a.id} (words on your brand's colours; free, no AI picture, in the background)${a.replace ? ", stopping the one being made for another post" : ""}.` : `Make a new picture for post #${a.id}${a.instruction ? `: ${String(a.instruction).slice(0, 300)}` : ""} (1 AI generation, in the background)${a.replace ? ", stopping the one being made for another post" : ""}.`,
|
|
8818
|
+
"zh-Hant": (a) => Number(a.count) >= 5 ? `\u70BA\u8CBC\u6587 #${a.id} \u505A ${a.count} \u9801\u6587\u5B57\u8F2A\u64AD\uFF08\u54C1\u724C\u8272\u4E0A\u7684\u6587\u5B57\uFF1B\u514D\u8CBB\uFF0C\u4E0D\u7528 AI \u751F\u5716\uFF0C\u5728\u80CC\u666F\u9032\u884C\uFF09${a.replace ? "\uFF0C\u4E26\u505C\u6B62\u6B63\u5728\u70BA\u53E6\u4E00\u7BC7\u751F\u6210\u7684\u5716\u7247" : ""}\u3002` : `\u70BA\u8CBC\u6587 #${a.id} \u751F\u6210\u65B0\u5716\u7247${a.instruction ? `\uFF1A${String(a.instruction).slice(0, 300)}` : ""}\uFF08\u7528 1 \u6B21 AI \u751F\u6210\uFF0C\u5728\u80CC\u666F\u9032\u884C\uFF09${a.replace ? "\uFF0C\u4E26\u505C\u6B62\u6B63\u5728\u70BA\u53E6\u4E00\u7BC7\u751F\u6210\u7684\u5716\u7247" : ""}\u3002`
|
|
8792
8819
|
},
|
|
8793
8820
|
social_image_stop: {
|
|
8794
8821
|
en: () => "Stop the picture being made; the post keeps the one it had.",
|
|
@@ -8796,17 +8823,37 @@ var confirmCopy = {
|
|
|
8796
8823
|
},
|
|
8797
8824
|
social_pictures_attach: {
|
|
8798
8825
|
en: (a) => {
|
|
8799
|
-
const
|
|
8800
|
-
return `${a.append ? "Add" : "Put"} your ${
|
|
8826
|
+
const n2 = (Array.isArray(a.urls) ? a.urls.length : 0) + (Array.isArray(a.files) ? a.files.length : 0);
|
|
8827
|
+
return `${a.append ? "Add" : "Put"} your ${n2} picture${n2 === 1 ? "" : "s"} ${a.append ? "after the pictures on" : "on"} post #${a.id}${a.append ? "" : ", in place of its picture"} (free). On LinkedIn a set goes out as a swipeable PDF carousel.`;
|
|
8801
8828
|
},
|
|
8802
8829
|
"zh-Hant": (a) => {
|
|
8803
|
-
const
|
|
8804
|
-
return `${a.append ? `\u5728\u8CBC\u6587 #${a.id} \u73FE\u6709\u5716\u7247\u5F8C\u52A0\u4E0A` : `\u628A\u8CBC\u6587 #${a.id} \u7684\u5716\u7247\u63DB\u6210`}\u4F60\u7684 ${
|
|
8830
|
+
const n2 = (Array.isArray(a.urls) ? a.urls.length : 0) + (Array.isArray(a.files) ? a.files.length : 0);
|
|
8831
|
+
return `${a.append ? `\u5728\u8CBC\u6587 #${a.id} \u73FE\u6709\u5716\u7247\u5F8C\u52A0\u4E0A` : `\u628A\u8CBC\u6587 #${a.id} \u7684\u5716\u7247\u63DB\u6210`}\u4F60\u7684 ${n2} \u5F35\u5716\u7247\uFF08\u514D\u8CBB\uFF09\u3002\u5728 LinkedIn \u4E0A\uFF0C\u4E00\u7D44\u5716\u7247\u6703\u4EE5\u53EF\u6ED1\u52D5\u7684 PDF \u8F2A\u64AD\u767C\u51FA\u3002`;
|
|
8805
8832
|
}
|
|
8806
8833
|
},
|
|
8807
8834
|
social_carousel_pdf_attach: {
|
|
8808
|
-
en: (a) => `
|
|
8809
|
-
"zh-Hant": (a) =>
|
|
8835
|
+
en: (a) => `Your PDF becomes post #${a.id}'s carousel${a.title ? `, titled ${q(a.title)}` : ""}: its first pages become the post's pictures, and on LinkedIn it goes out as the PDF itself (free).`,
|
|
8836
|
+
"zh-Hant": (a) => `\u4F60\u7684 PDF \u6210\u70BA\u8CBC\u6587 #${a.id} \u7684\u8F2A\u64AD${a.title ? `\uFF0C\u6A19\u984C ${q(a.title)}` : ""}\uFF1A\u524D\u5E7E\u9801\u6703\u6210\u70BA\u8CBC\u6587\u7684\u5716\u7247\uFF0C\u5728 LinkedIn \u4E0A\u76F4\u63A5\u4EE5 PDF \u767C\u51FA\uFF08\u514D\u8CBB\uFF09\u3002`
|
|
8837
|
+
},
|
|
8838
|
+
social_tiktok_photo: {
|
|
8839
|
+
en: (a) => `Make a TikTok photo post from post #${a.id}'s pictures${a.caption ? " with your caption" : " (Maggie writes the caption: 1 AI generation)"}; it waits for your OK.`,
|
|
8840
|
+
"zh-Hant": (a) => `\u7528\u8CBC\u6587 #${a.id} \u7684\u5716\u7247\u505A\u4E00\u5247 TikTok \u5716\u7247\u8CBC\u6587${a.caption ? "\uFF0C\u7528\u4F60\u7684\u6587\u6848" : "\uFF08Maggie \u5BEB\u6587\u6848\uFF1A\u7528 1 \u6B21 AI \u751F\u6210\uFF09"}\uFF0C\u7B49\u4F60\u78BA\u8A8D\u5F8C\u624D\u767C\u3002`
|
|
8841
|
+
},
|
|
8842
|
+
ad_video_plan: {
|
|
8843
|
+
en: (a) => `Plan a ${a.seconds ?? 20}-second ${a.aspect === "16:9" ? "landscape" : "vertical"} ad video${a.item_id ? ` from post #${a.item_id}` : ""}${a.reference_url ? ", following the YouTube ad you gave" : ""} (1 AI generation; nothing is billed by the second yet). You'll see the storyboard and the cost first.`,
|
|
8844
|
+
"zh-Hant": (a) => `\u898F\u5283\u4E00\u652F ${a.seconds ?? 20} \u79D2${a.aspect === "16:9" ? "\u6A6B\u5411" : "\u76F4\u5411"}\u5EE3\u544A\u5F71\u7247${a.item_id ? `\uFF08\u6839\u64DA\u8CBC\u6587 #${a.item_id}\uFF09` : ""}${a.reference_url ? "\uFF0C\u53C3\u8003\u4F60\u7D66\u7684 YouTube \u5EE3\u544A\u7BC0\u594F" : ""}\uFF08\u7528 1 \u6B21 AI \u751F\u6210\uFF0C\u9084\u4E0D\u6703\u6309\u79D2\u8A08\u8CBB\uFF09\u3002\u6703\u5148\u7D66\u4F60\u770B\u5206\u93E1\u548C\u8CBB\u7528\u3002`
|
|
8845
|
+
},
|
|
8846
|
+
ad_video_change: {
|
|
8847
|
+
en: (a) => `Change ad video #${a.id}'s storyboard: ${keys(a).filter((k) => k !== "id").join(", ") || "nothing"}.`,
|
|
8848
|
+
"zh-Hant": (a) => `\u4FEE\u6539\u5EE3\u544A\u5F71\u7247 #${a.id} \u7684\u5206\u93E1\uFF1A${keys(a).filter((k) => k !== "id").join("\u3001") || "\u7121"}\u3002`
|
|
8849
|
+
},
|
|
8850
|
+
ad_video_make: {
|
|
8851
|
+
en: (a) => `Make ad video #${a.id} now. This costs about $${Number(a.estimate_usd ?? 0).toFixed(2)} (pictures, moving clips, voice and music), in the background.`,
|
|
8852
|
+
"zh-Hant": (a) => `\u73FE\u5728\u88FD\u4F5C\u5EE3\u544A\u5F71\u7247 #${a.id}\uFF0C\u8CBB\u7528\u7D04 $${Number(a.estimate_usd ?? 0).toFixed(2)}\uFF08\u5716\u7247\u3001\u52D5\u614B\u7247\u6BB5\u3001\u914D\u97F3\u548C\u914D\u6A02\uFF09\uFF0C\u5728\u80CC\u666F\u9032\u884C\u3002`
|
|
8853
|
+
},
|
|
8854
|
+
ad_video_stop: {
|
|
8855
|
+
en: (a) => `Stop ad video #${a.id} if it's being made (what's made is kept), or remove it from the list.`,
|
|
8856
|
+
"zh-Hant": (a) => `\u505C\u6B62\u88FD\u4F5C\u5EE3\u544A\u5F71\u7247 #${a.id}\uFF08\u5DF2\u505A\u597D\u7684\u90E8\u5206\u6703\u4FDD\u7559\uFF09\uFF0C\u6216\u628A\u5B83\u5F9E\u6E05\u55AE\u79FB\u9664\u3002`
|
|
8810
8857
|
},
|
|
8811
8858
|
social_carousel_set: {
|
|
8812
8859
|
en: (a) => `LinkedIn post #${a.id} goes out ${a.format === "images" ? "as several pictures" : a.format === "pdf" ? "as a swipeable PDF carousel" : "as it is set"}${a.title ? `, titled ${q(a.title)}` : ""}.`,
|
|
@@ -9030,17 +9077,17 @@ function confirmText(tool3, args, lang = "en") {
|
|
|
9030
9077
|
function costText(cost2, lang = "en") {
|
|
9031
9078
|
const parts = [];
|
|
9032
9079
|
if (cost2.aiGenerations) {
|
|
9033
|
-
const
|
|
9080
|
+
const n2 = cost2.aiGenerations;
|
|
9034
9081
|
const left = cost2.aiLeft;
|
|
9035
9082
|
parts.push(
|
|
9036
|
-
lang === "zh-Hant" ? `\u7528 ${
|
|
9083
|
+
lang === "zh-Hant" ? `\u7528 ${n2} \u6B21 AI \u751F\u6210${left == null ? "" : `\uFF08\u4ECA\u5929\u5269 ${left} \u6B21\uFF09`}\u3002` : `Uses ${n2} AI generation${n2 === 1 ? "" : "s"}${left == null ? "" : ` (${left} left today)`}.`
|
|
9037
9084
|
);
|
|
9038
9085
|
}
|
|
9039
9086
|
if (cost2.credits) {
|
|
9040
|
-
const
|
|
9087
|
+
const n2 = cost2.credits;
|
|
9041
9088
|
const left = cost2.creditsLeft;
|
|
9042
9089
|
parts.push(
|
|
9043
|
-
lang === "zh-Hant" ? `\u6263 ${
|
|
9090
|
+
lang === "zh-Hant" ? `\u6263 ${n2} \u500B\u984D\u5EA6${left == null ? "" : `\uFF08\u5269 ${left}\uFF09`}\u3002` : `Uses ${n2} credit${n2 === 1 ? "" : "s"}${left == null ? "" : ` (${left} left)`}.`
|
|
9044
9091
|
);
|
|
9045
9092
|
}
|
|
9046
9093
|
return parts.join(" ");
|
|
@@ -9052,13 +9099,13 @@ function ws(ctx) {
|
|
|
9052
9099
|
if (!ctx.domain) throw new Error("No website is selected yet. Add one first.");
|
|
9053
9100
|
return domainApi(ctx.api, ctx.domain);
|
|
9054
9101
|
}
|
|
9055
|
-
var clip = (text3,
|
|
9102
|
+
var clip = (text3, n2 = 280) => typeof text3 === "string" ? text3.length > n2 ? `${text3.slice(0, n2)}\u2026` : text3 : text3 ?? null;
|
|
9056
9103
|
var plain = (html) => String(html ?? "").replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
|
|
9057
9104
|
var untrusted = (value) => ({ untrusted: value });
|
|
9058
9105
|
function upsert(list2, item2, key) {
|
|
9059
9106
|
const k = (key(item2) ?? "").trim().toLowerCase();
|
|
9060
9107
|
const i = list2.findIndex((x) => (key(x) ?? "").trim().toLowerCase() === k);
|
|
9061
|
-
return i >= 0 ? list2.map((x,
|
|
9108
|
+
return i >= 0 ? list2.map((x, n2) => n2 === i ? { ...x, ...item2 } : x) : [...list2, item2];
|
|
9062
9109
|
}
|
|
9063
9110
|
function remove(list2, name, key) {
|
|
9064
9111
|
const k = name.trim().toLowerCase();
|
|
@@ -9067,8 +9114,8 @@ function remove(list2, name, key) {
|
|
|
9067
9114
|
return out;
|
|
9068
9115
|
}
|
|
9069
9116
|
var int = (v, min, max, fallback) => {
|
|
9070
|
-
const
|
|
9071
|
-
return Number.isFinite(
|
|
9117
|
+
const n2 = Math.trunc(Number(v));
|
|
9118
|
+
return Number.isFinite(n2) ? Math.min(max, Math.max(min, n2)) : fallback;
|
|
9072
9119
|
};
|
|
9073
9120
|
var positionParam = (what, fallback) => ({
|
|
9074
9121
|
type: "string",
|
|
@@ -9237,12 +9284,12 @@ var accountTools = [
|
|
|
9237
9284
|
risk: "R0",
|
|
9238
9285
|
run: async (ctx, { platform }) => {
|
|
9239
9286
|
const p = platform && CONNECT_FORMS.includes(platform) ? platform : "";
|
|
9240
|
-
const
|
|
9287
|
+
const label2 = p ? `Connect ${CONNECT_NAMES[p]}` : "Connect your website";
|
|
9241
9288
|
const link = `https://app.cmo.so/posts?connect=${p || "wordpress"}${ctx.domain ? `&site=${encodeURIComponent(ctx.domain)}` : ""}`;
|
|
9242
9289
|
if (ctx.client === "chat")
|
|
9243
9290
|
return {
|
|
9244
|
-
card: { type: "connect", platform: p || null, label },
|
|
9245
|
-
shown: `A "${
|
|
9291
|
+
card: { type: "connect", platform: p || null, label: label2 },
|
|
9292
|
+
shown: `A "${label2}" button now shows under your message. The user fills in the form there; say so in one line.`
|
|
9246
9293
|
};
|
|
9247
9294
|
return { link, shown: "Give the user this link: it opens the form in the cmo.so app (web or phone)." };
|
|
9248
9295
|
}
|
|
@@ -9945,10 +9992,10 @@ var brandTools = [
|
|
|
9945
9992
|
need: write2,
|
|
9946
9993
|
risk: "W1",
|
|
9947
9994
|
run: async (ctx, { count }) => {
|
|
9948
|
-
const
|
|
9949
|
-
if (!Number.isFinite(
|
|
9950
|
-
const out = await ws(ctx).updateBrand({ internal_links: { count:
|
|
9951
|
-
return { internal_links: out.internal_links ?? { count:
|
|
9995
|
+
const n2 = Math.round(Number(count));
|
|
9996
|
+
if (!Number.isFinite(n2) || n2 < 0 || n2 > 10) throw new Error("count must be 0 to 10.");
|
|
9997
|
+
const out = await ws(ctx).updateBrand({ internal_links: { count: n2 } });
|
|
9998
|
+
return { internal_links: out.internal_links ?? { count: n2 } };
|
|
9952
9999
|
}
|
|
9953
10000
|
}),
|
|
9954
10001
|
tool({
|
|
@@ -9969,8 +10016,8 @@ var brandTools = [
|
|
|
9969
10016
|
risk: "W1",
|
|
9970
10017
|
run: async (ctx, { use_in_posts, ...item2 }) => {
|
|
9971
10018
|
const t = (await ws(ctx).brand()).testimonials ?? { enabled: false, items: [] };
|
|
9972
|
-
const
|
|
9973
|
-
return (await ws(ctx).updateBrand({ testimonials: { enabled: use_in_posts ?? t.enabled, items } })).testimonials;
|
|
10019
|
+
const items2 = upsert(t.items, item2, (i) => i.name);
|
|
10020
|
+
return (await ws(ctx).updateBrand({ testimonials: { enabled: use_in_posts ?? t.enabled, items: items2 } })).testimonials;
|
|
9974
10021
|
}
|
|
9975
10022
|
}),
|
|
9976
10023
|
tool({
|
|
@@ -9988,8 +10035,8 @@ var brandTools = [
|
|
|
9988
10035
|
risk: "W1",
|
|
9989
10036
|
run: async (ctx, { name, use_in_posts }) => {
|
|
9990
10037
|
const t = (await ws(ctx).brand()).testimonials ?? { enabled: false, items: [] };
|
|
9991
|
-
const
|
|
9992
|
-
return (await ws(ctx).updateBrand({ testimonials: { enabled: use_in_posts ?? t.enabled, items } })).testimonials;
|
|
10038
|
+
const items2 = name ? remove(t.items, name, (i) => i.name) : t.items;
|
|
10039
|
+
return (await ws(ctx).updateBrand({ testimonials: { enabled: use_in_posts ?? t.enabled, items: items2 } })).testimonials;
|
|
9993
10040
|
}
|
|
9994
10041
|
}),
|
|
9995
10042
|
tool({
|
|
@@ -10191,7 +10238,7 @@ var brandTools = [
|
|
|
10191
10238
|
);
|
|
10192
10239
|
if (typeof entry === "string") throw new Error(entry);
|
|
10193
10240
|
if (old && sitemapUrl(old.url) !== entry.url) delete entry.check;
|
|
10194
|
-
const next = old ? list2.map((x,
|
|
10241
|
+
const next = old ? list2.map((x, n2) => n2 === i ? entry : x) : [...list2, entry];
|
|
10195
10242
|
const problem = checkSitemaps(next);
|
|
10196
10243
|
if (problem) throw new Error(problem);
|
|
10197
10244
|
const saved = (await ws(ctx).updateBrand({ sitemaps: next })).sitemaps ?? next;
|
|
@@ -10215,7 +10262,7 @@ var brandTools = [
|
|
|
10215
10262
|
const list2 = (await ws(ctx).brand()).sitemaps ?? [];
|
|
10216
10263
|
const i = findSitemap(list2, id, url);
|
|
10217
10264
|
if (i < 0) throw new Error(`No sitemap ${id ?? url} was found. Use sitemap_list.`);
|
|
10218
|
-
const saved = (await ws(ctx).updateBrand({ sitemaps: list2.filter((_,
|
|
10265
|
+
const saved = (await ws(ctx).updateBrand({ sitemaps: list2.filter((_, n2) => n2 !== i) })).sitemaps ?? [];
|
|
10219
10266
|
return { removed: list2[i]?.url, sitemaps: saved.map((s) => s.url) };
|
|
10220
10267
|
}
|
|
10221
10268
|
}),
|
|
@@ -10374,7 +10421,7 @@ function sitemapResult(s) {
|
|
|
10374
10421
|
// ../../packages/tools/src/feedback.ts
|
|
10375
10422
|
var account = { resource: "account", level: "read" };
|
|
10376
10423
|
var TYPES = ["bug", "quality", "idea", "praise", "other"];
|
|
10377
|
-
var text = (v,
|
|
10424
|
+
var text = (v, n2) => String(v ?? "").trim().slice(0, n2);
|
|
10378
10425
|
var feedbackTools = [
|
|
10379
10426
|
tool({
|
|
10380
10427
|
name: "feedback_send",
|
|
@@ -11566,6 +11613,36 @@ var CHANNEL_KEYS = ["linkedin", "facebook", "instagram", "google_business"];
|
|
|
11566
11613
|
var ALL_CHANNEL_KEYS = [...CHANNEL_KEYS, "youtube", "tiktok"];
|
|
11567
11614
|
var VIDEO_KEYS = ["linkedin", "facebook", "instagram", "youtube", "tiktok"];
|
|
11568
11615
|
var TIKTOK_PRIVACY = ["PUBLIC_TO_EVERYONE", "MUTUAL_FOLLOW_FRIENDS", "FOLLOWER_OF_CREATOR", "SELF_ONLY"];
|
|
11616
|
+
var SLIDES = [5, 6, 7, 8, 9, 10];
|
|
11617
|
+
var adVideo = (v) => ({
|
|
11618
|
+
id: v.id,
|
|
11619
|
+
status: v.status,
|
|
11620
|
+
step: v.step,
|
|
11621
|
+
aspect: v.aspect,
|
|
11622
|
+
plan: v.plan ? {
|
|
11623
|
+
title: v.plan.title,
|
|
11624
|
+
style: v.plan.style,
|
|
11625
|
+
voice: v.plan.voice,
|
|
11626
|
+
music: v.plan.music,
|
|
11627
|
+
seconds: v.plan.seconds,
|
|
11628
|
+
end_card: v.plan.cta,
|
|
11629
|
+
scenes: v.plan.scenes.map((s) => ({
|
|
11630
|
+
id: s.id,
|
|
11631
|
+
seconds: s.seconds,
|
|
11632
|
+
picture: s.still,
|
|
11633
|
+
motion: s.motion,
|
|
11634
|
+
line: s.line,
|
|
11635
|
+
caption: s.caption,
|
|
11636
|
+
picture_url: s.still_url ?? null
|
|
11637
|
+
}))
|
|
11638
|
+
} : null,
|
|
11639
|
+
estimate: v.estimate,
|
|
11640
|
+
cost_usd: v.cost_usd,
|
|
11641
|
+
video_id: v.video_id,
|
|
11642
|
+
video: v.video ? { status: v.video.status, preview_url: v.video.preview_url, poster_url: v.video.poster_url } : null,
|
|
11643
|
+
error: v.error,
|
|
11644
|
+
next: v.status === "planned" ? `Show the storyboard and the cost (about $${(v.estimate?.usd ?? 0).toFixed(2)}); change it with ad_video_change, make it with ad_video_make when the user agrees to that cost.` : v.status === "running" ? "Being made; check again with ad_video_get in a minute or so." : v.status === "ready" ? "Ready: post it with social_video_add (video_id and channels) once video.status is ready." : v.status === "failed" ? "Failed: say why; ad_video_make again carries on from what was made." : ""
|
|
11645
|
+
});
|
|
11569
11646
|
var channelsParam = {
|
|
11570
11647
|
type: "array",
|
|
11571
11648
|
items: { type: "string", enum: CHANNEL_KEYS },
|
|
@@ -11582,6 +11659,11 @@ var FILE_SCHEMA = {
|
|
|
11582
11659
|
required: ["download_url", "file_id"]
|
|
11583
11660
|
};
|
|
11584
11661
|
var fileLinks = (value) => (Array.isArray(value) ? value : value ? [value] : []).map((f) => String(f?.download_url ?? "").trim()).filter(Boolean);
|
|
11662
|
+
var videoLink = (a) => {
|
|
11663
|
+
const link = fileLinks(a.file)[0] ?? String(a.url ?? "").trim();
|
|
11664
|
+
if (!link.startsWith("https://")) throw new Error("Give the video (a file from this chat, or a direct https link).");
|
|
11665
|
+
return link;
|
|
11666
|
+
};
|
|
11585
11667
|
var queuedAt = positionParam("the posts", "Leave out for the front (the next posting times).");
|
|
11586
11668
|
var channelsOf = (value) => {
|
|
11587
11669
|
const list2 = Array.isArray(value) ? value.map((v) => String(v).toLowerCase()).filter((v) => CHANNEL_KEYS.includes(v)) : [];
|
|
@@ -11694,12 +11776,12 @@ var socialTools = [
|
|
|
11694
11776
|
const at = positionOf(position);
|
|
11695
11777
|
const api2 = ws(ctx);
|
|
11696
11778
|
const results = [];
|
|
11697
|
-
for (const
|
|
11779
|
+
for (const n2 of list2) {
|
|
11698
11780
|
try {
|
|
11699
|
-
await api2.approveSocial(
|
|
11700
|
-
results.push({ id:
|
|
11781
|
+
await api2.approveSocial(n2, at);
|
|
11782
|
+
results.push({ id: n2, ok: true });
|
|
11701
11783
|
} catch (e) {
|
|
11702
|
-
results.push({ id:
|
|
11784
|
+
results.push({ id: n2, ok: false, error: e instanceof Error ? e.message : String(e) });
|
|
11703
11785
|
}
|
|
11704
11786
|
}
|
|
11705
11787
|
return { approved: results.filter((r) => r.ok).length, results };
|
|
@@ -11714,9 +11796,9 @@ var socialTools = [
|
|
|
11714
11796
|
risk: "W3",
|
|
11715
11797
|
gates: ["VER"],
|
|
11716
11798
|
run: async (ctx, { id }) => {
|
|
11717
|
-
const
|
|
11718
|
-
if (!Number.isInteger(
|
|
11719
|
-
const post = await ws(ctx).publishSocialNow(
|
|
11799
|
+
const n2 = Number(id);
|
|
11800
|
+
if (!Number.isInteger(n2) || n2 <= 0) throw new Error("Which post?");
|
|
11801
|
+
const post = await ws(ctx).publishSocialNow(n2, { background: true });
|
|
11720
11802
|
return post.status === "published" ? post : { ...post, note: "Posting now. Check social_post_get in a minute for its link." };
|
|
11721
11803
|
}
|
|
11722
11804
|
}),
|
|
@@ -11737,11 +11819,11 @@ var socialTools = [
|
|
|
11737
11819
|
risk: "W3",
|
|
11738
11820
|
gates: ["VER"],
|
|
11739
11821
|
run: async (ctx, { id, social_copy, repost }) => {
|
|
11740
|
-
const
|
|
11741
|
-
if (!Number.isInteger(
|
|
11822
|
+
const n2 = Number(id);
|
|
11823
|
+
if (!Number.isInteger(n2) || n2 <= 0) throw new Error("Which post?");
|
|
11742
11824
|
const copy = typeof social_copy === "string" ? social_copy.trim() : "";
|
|
11743
11825
|
if (!copy && repost !== true) throw new Error("Give the new text (social_copy), or repost: true.");
|
|
11744
|
-
return ws(ctx).republishSocial(
|
|
11826
|
+
return ws(ctx).republishSocial(n2, copy || void 0, repost === true);
|
|
11745
11827
|
}
|
|
11746
11828
|
}),
|
|
11747
11829
|
tool({
|
|
@@ -11753,9 +11835,9 @@ var socialTools = [
|
|
|
11753
11835
|
risk: "W3",
|
|
11754
11836
|
gates: ["VER"],
|
|
11755
11837
|
run: async (ctx, { id }) => {
|
|
11756
|
-
const
|
|
11757
|
-
if (!Number.isInteger(
|
|
11758
|
-
return ws(ctx).deletePostedSocial(
|
|
11838
|
+
const n2 = Number(id);
|
|
11839
|
+
if (!Number.isInteger(n2) || n2 <= 0) throw new Error("Which post?");
|
|
11840
|
+
return ws(ctx).deletePostedSocial(n2);
|
|
11759
11841
|
}
|
|
11760
11842
|
}),
|
|
11761
11843
|
tool({
|
|
@@ -11780,7 +11862,7 @@ var socialTools = [
|
|
|
11780
11862
|
}),
|
|
11781
11863
|
tool({
|
|
11782
11864
|
name: "social_image_new",
|
|
11783
|
-
description: "Make a new picture for a social post that hasn't gone out, or change its current one (mode: edit, as `instruction` says). Uses 1 AI generation. It is made in the background, on the user's own ChatGPT when their Maggie Bridge has images on, else by CMO.so; it takes up to a couple of minutes and appears on the post by itself (check with social_image_status). One picture is made at a time per website: while another is being made this one waits in line (status queued). It fails with image_busy only when this post already has one being made or in line; pass replace: true only when the user agrees to stop the one being made and make this one now. A post written from an article gets its own picture (from its text and the article's title) and then goes out as that picture. Every new picture carries a short title and line set on it; count: 4 makes a set of 4 pictures that tell one story (still 1 AI generation, drawn as one storyboard and cut); LinkedIn and Facebook post a set together as a collage. text_effect picks the style of those words (an id from social_text_effects); leave it out to let Maggie pick. style picks the picture style (an id from social_look: photo_headline, editorial, duotone, illustrated, bold_type, infographic); leave it out and Maggie takes turns with the brand's 3. bold_type and infographic make no AI picture (big words or a list in the brand's look) and cost nothing.",
|
|
11865
|
+
description: "Make a new picture for a social post that hasn't gone out, or change its current one (mode: edit, as `instruction` says). Uses 1 AI generation. It is made in the background, on the user's own ChatGPT when their Maggie Bridge has images on, else by CMO.so; it takes up to a couple of minutes and appears on the post by itself (check with social_image_status). One picture is made at a time per website: while another is being made this one waits in line (status queued). It fails with image_busy only when this post already has one being made or in line; pass replace: true only when the user agrees to stop the one being made and make this one now. A post written from an article gets its own picture (from its text and the article's title) and then goes out as that picture. Every new picture carries a short title and line set on it; count: 4 makes a set of 4 pictures that tell one story (still 1 AI generation, drawn as one storyboard and cut); LinkedIn and Facebook post a set together as a collage. text_effect picks the style of those words (an id from social_text_effects); leave it out to let Maggie pick. style picks the picture style (an id from social_look: photo_headline, editorial, duotone, illustrated, bold_type, infographic); leave it out and Maggie takes turns with the brand's 3. bold_type and infographic make no AI picture (big words or a list in the brand's look) and cost nothing. count 5-10 makes a text carousel: a cover with the hook, one point a slide and a last slide with the next step, words on the brand's colours (style bold_type or infographic), no AI picture and free; LinkedIn posts it as a swipeable PDF carousel, Instagram and Facebook as a carousel (the first 10), TikTok as a photo post (social_tiktok_photo).",
|
|
11784
11866
|
parameters: {
|
|
11785
11867
|
type: "object",
|
|
11786
11868
|
properties: {
|
|
@@ -11797,7 +11879,7 @@ var socialTools = [
|
|
|
11797
11879
|
},
|
|
11798
11880
|
count: {
|
|
11799
11881
|
type: "integer",
|
|
11800
|
-
description: "1 (default)
|
|
11882
|
+
description: "1 (default); 4: a set of 4 pictures telling one story, a line of words on each; 5-10: a text carousel of that many slides (free)"
|
|
11801
11883
|
},
|
|
11802
11884
|
text_effect: {
|
|
11803
11885
|
type: "string",
|
|
@@ -11819,7 +11901,7 @@ var socialTools = [
|
|
|
11819
11901
|
replace: replace === true,
|
|
11820
11902
|
...typeof style === "string" && style.trim() && mode !== "edit" ? { style: style.trim() } : {},
|
|
11821
11903
|
...mode === "edit" ? { mode: "edit" } : {},
|
|
11822
|
-
...Number(count) === 4
|
|
11904
|
+
...mode !== "edit" && (Number(count) === 4 || SLIDES.includes(Number(count))) ? { count: Number(count) } : {},
|
|
11823
11905
|
...typeof text_effect === "string" && text_effect.trim() ? { textEffect: text_effect.trim() } : {},
|
|
11824
11906
|
...typeof instruction === "string" && instruction.trim() ? { instruction: instruction.trim().slice(0, 300) } : {}
|
|
11825
11907
|
});
|
|
@@ -12045,11 +12127,14 @@ var socialTools = [
|
|
|
12045
12127
|
}),
|
|
12046
12128
|
tool({
|
|
12047
12129
|
name: "social_carousel_pdf_attach",
|
|
12048
|
-
description: "A
|
|
12130
|
+
description: "A PDF carousel for a LinkedIn, Instagram, Facebook or TikTok post: the PDF from this chat (file: one the user added or that was made here) or a public https link to it (url); up to 300 pages and 20 MB, no password. Its first 20 pages become the post's pictures: Instagram and Facebook post them as a carousel (the first 10), TikTok as a photo post. A LinkedIn post (this one, or the same idea's LinkedIn post) goes out as the PDF itself, a swipeable document carousel. Free. Change the pictures later and the LinkedIn carousel is made from them instead. If you can pass neither, give the user social_upload_link. Preview first.",
|
|
12049
12131
|
parameters: {
|
|
12050
12132
|
type: "object",
|
|
12051
12133
|
properties: {
|
|
12052
|
-
id: {
|
|
12134
|
+
id: {
|
|
12135
|
+
type: "integer",
|
|
12136
|
+
description: "A LinkedIn, Instagram, Facebook or TikTok post (from social_list), not gone out yet"
|
|
12137
|
+
},
|
|
12053
12138
|
file: { ...FILE_SCHEMA, description: "The PDF from this chat" },
|
|
12054
12139
|
url: { type: "string", description: "Public https link to the PDF file" },
|
|
12055
12140
|
title: {
|
|
@@ -12063,7 +12148,7 @@ var socialTools = [
|
|
|
12063
12148
|
group: "social",
|
|
12064
12149
|
need: write5,
|
|
12065
12150
|
risk: "W1",
|
|
12066
|
-
gates: ["VER"
|
|
12151
|
+
gates: ["VER"],
|
|
12067
12152
|
run: async (ctx, { id, url, file, title }) => {
|
|
12068
12153
|
const link = fileLinks(file)[0] ?? String(url ?? "").trim();
|
|
12069
12154
|
if (!link.startsWith("https://"))
|
|
@@ -12092,7 +12177,7 @@ var socialTools = [
|
|
|
12092
12177
|
id: post.id,
|
|
12093
12178
|
channel: post.provider ?? null,
|
|
12094
12179
|
url: `https://app.cmo.so/social?picture=${post.id}${site}`,
|
|
12095
|
-
how:
|
|
12180
|
+
how: ["linkedin", "instagram", "facebook", "tiktok"].includes(String(post.provider)) ? "Open it, then Upload several photos (in order), or on a computer Upload a PDF carousel." : "Open it, then Upload several photos (in order)."
|
|
12096
12181
|
};
|
|
12097
12182
|
}
|
|
12098
12183
|
}),
|
|
@@ -12790,22 +12875,24 @@ var socialTools = [
|
|
|
12790
12875
|
}),
|
|
12791
12876
|
tool({
|
|
12792
12877
|
name: "social_video_attach",
|
|
12793
|
-
description: "Make a LinkedIn, Facebook or Instagram post that hasn't gone out a video post: it goes out as this video (a DIRECT link to a video file
|
|
12878
|
+
description: "Make a LinkedIn, Facebook or Instagram post that hasn't gone out a video post: it goes out as this video (5-30 seconds) with its text, and its picture is removed (a video post has no picture). The video from this chat (file: one the user added) or a DIRECT https link to a video file (url; never a platform page). If you can pass neither, the user adds the video in the CMO.so app (Social). Not for Google Business (posts can't carry video). Preview first.",
|
|
12794
12879
|
parameters: {
|
|
12795
12880
|
type: "object",
|
|
12796
12881
|
properties: {
|
|
12797
12882
|
id: { type: "integer", description: "The post (from social_list)" },
|
|
12883
|
+
file: { ...FILE_SCHEMA, description: "The video from this chat (MP4 or MOV)" },
|
|
12798
12884
|
url: { type: "string", description: "Direct https link to the video file" }
|
|
12799
12885
|
},
|
|
12800
|
-
required: ["id"
|
|
12886
|
+
required: ["id"]
|
|
12801
12887
|
},
|
|
12888
|
+
fileParams: ["file"],
|
|
12802
12889
|
group: "social",
|
|
12803
12890
|
need: write5,
|
|
12804
12891
|
risk: "W1",
|
|
12805
12892
|
gates: ["VER"],
|
|
12806
12893
|
run: async (ctx, a) => {
|
|
12807
12894
|
const d = ws(ctx);
|
|
12808
|
-
let v = await d.videoFromLink(
|
|
12895
|
+
let v = await d.videoFromLink(videoLink(a));
|
|
12809
12896
|
const until = Date.now() + 15e4;
|
|
12810
12897
|
while ((v.status === "checking" || v.status === "uploading") && Date.now() < until) {
|
|
12811
12898
|
await new Promise((r) => setTimeout(r, 3e3));
|
|
@@ -12881,11 +12968,13 @@ var socialTools = [
|
|
|
12881
12968
|
}),
|
|
12882
12969
|
tool({
|
|
12883
12970
|
name: "social_video_add",
|
|
12884
|
-
description: "Make video posts (channels: linkedin, facebook, instagram, youtube, tiktok; default youtube; one post each, never google_business: Google posts can't carry video) from a video: a DIRECT link to a video file (an .mp4 on their website, a public Dropbox link), never a YouTube / TikTok / Instagram page (we never download those
|
|
12971
|
+
description: "Make video posts (channels: linkedin, facebook, instagram, youtube, tiktok; default youtube; one post each, never google_business: Google posts can't carry video) from a video: a video already in CMO.so (video_id, e.g. a ready ad video's), the video from this chat (file: one the user added) or a DIRECT https link to a video file (url: an .mp4 on their website, a public Dropbox link), never a YouTube / TikTok / Instagram page (we never download those). If you can pass neither, tell the user to add the video in the CMO.so app (Social). The video must be 5-30 seconds: give start/end (seconds) to use a part of a longer one. vertical: make a wide video vertical (a Short) cropped at left, middle or right. Maggie writes YouTube's title, description and tags, TikTok's caption, or the LinkedIn / Facebook / Instagram text, from the video and the note (one AI generation per channel) unless write is false. Each post waits in Review: video posts are always approved by the user. YouTube asks whether it's made for kids; TikTok asks who can view it and the other TikTok settings (social_tiktok_set, as the user chooses). is_aigc: TikTok's AI-generated label, only when the user says the video was made with AI. Preview first.",
|
|
12885
12972
|
parameters: {
|
|
12886
12973
|
type: "object",
|
|
12887
12974
|
properties: {
|
|
12975
|
+
file: { ...FILE_SCHEMA, description: "The video from this chat (MP4 or MOV)" },
|
|
12888
12976
|
url: { type: "string", description: "Direct https link to the video file" },
|
|
12977
|
+
video_id: { type: "integer", description: "A video already in CMO.so (an ad video's video_id)" },
|
|
12889
12978
|
channels: {
|
|
12890
12979
|
type: "array",
|
|
12891
12980
|
items: { type: "string", enum: [...VIDEO_KEYS] },
|
|
@@ -12897,9 +12986,9 @@ var socialTools = [
|
|
|
12897
12986
|
start: { type: "number", description: "Start of the part to use, seconds" },
|
|
12898
12987
|
end: { type: "number", description: "End of the part to use, seconds" },
|
|
12899
12988
|
vertical: { type: "string", enum: ["left", "middle", "right"] }
|
|
12900
|
-
}
|
|
12901
|
-
required: ["url"]
|
|
12989
|
+
}
|
|
12902
12990
|
},
|
|
12991
|
+
fileParams: ["file"],
|
|
12903
12992
|
group: "social",
|
|
12904
12993
|
need: write5,
|
|
12905
12994
|
risk: "W2",
|
|
@@ -12911,7 +13000,7 @@ var socialTools = [
|
|
|
12911
13000
|
...a.end != null ? { end: Number(a.end) } : {},
|
|
12912
13001
|
...a.vertical ? { aspect: "9:16", crop_x: a.vertical === "left" ? 0 : a.vertical === "right" ? 1 : 0.5 } : {}
|
|
12913
13002
|
};
|
|
12914
|
-
let v = await d.
|
|
13003
|
+
let v = a.video_id ? await d.video(Number(a.video_id)) : await d.videoFromLink(videoLink(a), Object.keys(edit).length ? edit : void 0);
|
|
12915
13004
|
const until = Date.now() + 15e4;
|
|
12916
13005
|
while ((v.status === "checking" || v.status === "uploading") && Date.now() < until) {
|
|
12917
13006
|
await new Promise((r) => setTimeout(r, 3e3));
|
|
@@ -12944,6 +13033,183 @@ var socialTools = [
|
|
|
12944
13033
|
};
|
|
12945
13034
|
}
|
|
12946
13035
|
}),
|
|
13036
|
+
tool({
|
|
13037
|
+
name: "social_tiktok_photo",
|
|
13038
|
+
description: "A TikTok photo post (a swipeable set of pictures; TikTok adds music) from a post's pictures: a set, a text carousel (social_image_new count 5-10) or the pages of a PDF. A new TikTok post that waits in Review like every TikTok post. Maggie writes the caption from the post (one AI generation) unless caption is given. Then TikTok's settings: read tiktok_creator, ask who can view it and the rest, social_tiktok_set exactly as the user says. TikTok is in beta. Preview first.",
|
|
13039
|
+
parameters: {
|
|
13040
|
+
type: "object",
|
|
13041
|
+
properties: {
|
|
13042
|
+
id: { type: "integer", description: "A post with pictures (from social_list), not a video post" },
|
|
13043
|
+
note: { type: "string", description: "What to say in the caption, in the user's words" },
|
|
13044
|
+
caption: { type: "string", description: "The caption itself, when the user wrote it (no AI)" }
|
|
13045
|
+
},
|
|
13046
|
+
required: ["id"]
|
|
13047
|
+
},
|
|
13048
|
+
group: "social",
|
|
13049
|
+
need: write5,
|
|
13050
|
+
risk: "W2",
|
|
13051
|
+
gates: ["VER", "LLM"],
|
|
13052
|
+
run: async (ctx, { id, note, caption }) => {
|
|
13053
|
+
const out = await ws(ctx).tiktokPhotoPost(Number(id), {
|
|
13054
|
+
...note ? { note: String(note).slice(0, 1e3) } : {},
|
|
13055
|
+
...typeof caption === "string" && caption.trim() ? { caption: caption.trim(), write: false } : {}
|
|
13056
|
+
});
|
|
13057
|
+
return {
|
|
13058
|
+
post: item(out),
|
|
13059
|
+
next: "Show the caption; read tiktok_creator and ask who can view it, whether to allow comments and whether it promotes a brand, then social_tiktok_set exactly as they say; tell them the Music Usage Confirmation line. The user approves it."
|
|
13060
|
+
};
|
|
13061
|
+
}
|
|
13062
|
+
}),
|
|
13063
|
+
tool({
|
|
13064
|
+
name: "ad_video_plan",
|
|
13065
|
+
description: "Plan a short ad video (beta) and say what making it costs. Maggie writes a storyboard: 2-5 scenes, each with the first picture's brief (no words in pictures), what moves and one camera move, the narrator's line and the words on screen; an end card with the next step and the logo; a voice and music. One AI generation; nothing is billed by the second yet. brief: what the ad should do, in the user's words, or item_id: a post whose text is the brief. aspect 9:16 (Reels, TikTok, Shorts; default) or 16:9 (LinkedIn, YouTube); seconds 15, 20 (default) or 30; reference_url: a public YouTube ad whose rhythm to follow (Gemini watches it; never downloaded). Show the storyboard and the cost (estimate.items, estimate.usd) before ad_video_make. Preview first.",
|
|
13066
|
+
parameters: {
|
|
13067
|
+
type: "object",
|
|
13068
|
+
properties: {
|
|
13069
|
+
brief: { type: "string", description: "What the ad should do, who it's for, the offer (user's words)" },
|
|
13070
|
+
item_id: { type: "integer", description: "A post (from social_list) to make the ad from" },
|
|
13071
|
+
aspect: { type: "string", enum: ["9:16", "16:9"] },
|
|
13072
|
+
seconds: { type: "integer", description: "15, 20 or 30" },
|
|
13073
|
+
scenes: { type: "integer", description: "2-5 (default 3, or 4 for 30 seconds)" },
|
|
13074
|
+
reference_url: { type: "string", description: "A public YouTube ad to follow the rhythm of" }
|
|
13075
|
+
}
|
|
13076
|
+
},
|
|
13077
|
+
group: "social",
|
|
13078
|
+
need: write5,
|
|
13079
|
+
risk: "W2",
|
|
13080
|
+
gates: ["VER", "LLM"],
|
|
13081
|
+
run: async (ctx, a) => {
|
|
13082
|
+
const out = await ws(ctx).planAdVideo({
|
|
13083
|
+
...a.brief ? { brief: String(a.brief).slice(0, 3e3) } : {},
|
|
13084
|
+
...a.item_id ? { item_id: Number(a.item_id) } : {},
|
|
13085
|
+
...a.aspect ? { aspect: a.aspect } : {},
|
|
13086
|
+
...a.seconds ? { seconds: Number(a.seconds) } : {},
|
|
13087
|
+
...a.scenes ? { scenes: Number(a.scenes) } : {},
|
|
13088
|
+
...a.reference_url ? { reference_url: String(a.reference_url).trim() } : {}
|
|
13089
|
+
});
|
|
13090
|
+
return adVideo(out);
|
|
13091
|
+
}
|
|
13092
|
+
}),
|
|
13093
|
+
tool({
|
|
13094
|
+
name: "ad_video_list",
|
|
13095
|
+
description: "This website's ad videos (beta): each with its status (planned, running and its step, ready, failed), the storyboard's title, its cost so far and, when ready, the video (video_id) to post.",
|
|
13096
|
+
parameters: none,
|
|
13097
|
+
group: "social",
|
|
13098
|
+
need: read5,
|
|
13099
|
+
risk: "R0",
|
|
13100
|
+
run: async (ctx) => {
|
|
13101
|
+
const out = await ws(ctx).adVideos();
|
|
13102
|
+
return {
|
|
13103
|
+
ad_videos: out.ad_videos.map((v) => ({
|
|
13104
|
+
id: v.id,
|
|
13105
|
+
status: v.status,
|
|
13106
|
+
step: v.step,
|
|
13107
|
+
title: v.plan?.title ?? null,
|
|
13108
|
+
aspect: v.aspect,
|
|
13109
|
+
cost_usd: v.cost_usd,
|
|
13110
|
+
video_id: v.video_id,
|
|
13111
|
+
video_status: v.video?.status ?? null,
|
|
13112
|
+
error: v.error
|
|
13113
|
+
})),
|
|
13114
|
+
voices: out.voices
|
|
13115
|
+
};
|
|
13116
|
+
}
|
|
13117
|
+
}),
|
|
13118
|
+
tool({
|
|
13119
|
+
name: "ad_video_get",
|
|
13120
|
+
description: "One ad video (beta): its storyboard (scenes with their pictures once made), what making the missing pieces costs (estimate), its status and step; when ready, video_id and the video's status (it is prepared for the channels in the background: post it once video.status is ready). While it's running, check again every minute or so.",
|
|
13121
|
+
parameters: {
|
|
13122
|
+
type: "object",
|
|
13123
|
+
properties: { id: { type: "integer", description: "The ad video (from ad_video_list)" } },
|
|
13124
|
+
required: ["id"]
|
|
13125
|
+
},
|
|
13126
|
+
group: "social",
|
|
13127
|
+
need: read5,
|
|
13128
|
+
risk: "R0",
|
|
13129
|
+
run: async (ctx, { id }) => adVideo(await ws(ctx).adVideo(Number(id)))
|
|
13130
|
+
}),
|
|
13131
|
+
tool({
|
|
13132
|
+
name: "ad_video_change",
|
|
13133
|
+
description: "Change an ad video's storyboard (beta) as the user asks: a scene's picture brief (still), its movement (motion), the narrator's line, the words on screen (caption) or its seconds (3-8); redo_scenes to make scenes' pictures and clips again; the end card (cta_headline, cta_sub); the voice (a name from ad_video_list voices, or none); the music (words, or none). Only what a change touches is made again (redo): a picture brief or movement means a new paid clip; lines, words, end card, voice or music are cheap. Says the new cost; then ad_video_make. Not while it's being made. Preview first.",
|
|
13134
|
+
parameters: {
|
|
13135
|
+
type: "object",
|
|
13136
|
+
properties: {
|
|
13137
|
+
id: { type: "integer", description: "The ad video" },
|
|
13138
|
+
scenes: {
|
|
13139
|
+
type: "array",
|
|
13140
|
+
items: {
|
|
13141
|
+
type: "object",
|
|
13142
|
+
properties: {
|
|
13143
|
+
id: { type: "string", description: "Scene id, e.g. s2" },
|
|
13144
|
+
still: { type: "string" },
|
|
13145
|
+
motion: { type: "string" },
|
|
13146
|
+
line: { type: "string" },
|
|
13147
|
+
caption: { type: "string" },
|
|
13148
|
+
seconds: { type: "number" }
|
|
13149
|
+
},
|
|
13150
|
+
required: ["id"]
|
|
13151
|
+
}
|
|
13152
|
+
},
|
|
13153
|
+
redo_scenes: { type: "array", items: { type: "string" } },
|
|
13154
|
+
cta_headline: { type: "string" },
|
|
13155
|
+
cta_sub: { type: "string" },
|
|
13156
|
+
voice: { type: "string", description: "A voice name, or none" },
|
|
13157
|
+
music: { type: "string", description: "The music in words, or none" },
|
|
13158
|
+
title: { type: "string" }
|
|
13159
|
+
},
|
|
13160
|
+
required: ["id"]
|
|
13161
|
+
},
|
|
13162
|
+
group: "social",
|
|
13163
|
+
need: write5,
|
|
13164
|
+
risk: "W1",
|
|
13165
|
+
gates: ["VER"],
|
|
13166
|
+
run: async (ctx, a) => {
|
|
13167
|
+
const cta = {
|
|
13168
|
+
...a.cta_headline != null ? { headline: a.cta_headline } : {},
|
|
13169
|
+
...a.cta_sub != null ? { sub: a.cta_sub } : {}
|
|
13170
|
+
};
|
|
13171
|
+
const out = await ws(ctx).changeAdVideo(Number(a.id), {
|
|
13172
|
+
...a.scenes?.length ? { scenes: a.scenes } : {},
|
|
13173
|
+
...a.redo_scenes?.length ? { redo_scenes: a.redo_scenes } : {},
|
|
13174
|
+
...Object.keys(cta).length ? { cta } : {},
|
|
13175
|
+
...a.voice != null ? { voice: /^none$/i.test(a.voice) ? false : a.voice } : {},
|
|
13176
|
+
...a.music != null ? { music: /^none$/i.test(a.music) ? "" : a.music } : {},
|
|
13177
|
+
...a.title ? { title: a.title } : {}
|
|
13178
|
+
});
|
|
13179
|
+
return { ...adVideo(out), redo: out.redo ?? [] };
|
|
13180
|
+
}
|
|
13181
|
+
}),
|
|
13182
|
+
tool({
|
|
13183
|
+
name: "ad_video_make",
|
|
13184
|
+
description: "Make the ad video (beta): its pictures (on the user's own ChatGPT when their Maggie Bridge has images on, else Gemini), the moving clips (Gemini, billed per second), the voice, the music, the words and the end card, cut into one video, in the background (several minutes; follow it with ad_video_get). It COSTS MONEY: estimate_usd must be the estimate.usd from ad_video_plan / ad_video_get / ad_video_change that the user agreed to; if the cost changed it is refused with the new one. Each website has a daily limit. Making it again after a change or a failure only makes what's missing. When ready, post it with social_video_add and its video_id. Preview first.",
|
|
13185
|
+
parameters: {
|
|
13186
|
+
type: "object",
|
|
13187
|
+
properties: {
|
|
13188
|
+
id: { type: "integer", description: "The ad video" },
|
|
13189
|
+
estimate_usd: { type: "number", description: "The cost the user agreed to (estimate.usd)" }
|
|
13190
|
+
},
|
|
13191
|
+
required: ["id", "estimate_usd"]
|
|
13192
|
+
},
|
|
13193
|
+
group: "social",
|
|
13194
|
+
need: write5,
|
|
13195
|
+
risk: "W3",
|
|
13196
|
+
gates: ["VER"],
|
|
13197
|
+
run: async (ctx, { id, estimate_usd }) => adVideo(await ws(ctx).makeAdVideo(Number(id), Number(estimate_usd)))
|
|
13198
|
+
}),
|
|
13199
|
+
tool({
|
|
13200
|
+
name: "ad_video_stop",
|
|
13201
|
+
description: "Stop an ad video being made (after the piece it's on; what was made is kept and making it again carries on), or remove one that isn't being made from the list (posts made from it keep their video). Preview first.",
|
|
13202
|
+
parameters: {
|
|
13203
|
+
type: "object",
|
|
13204
|
+
properties: { id: { type: "integer", description: "The ad video" } },
|
|
13205
|
+
required: ["id"]
|
|
13206
|
+
},
|
|
13207
|
+
group: "social",
|
|
13208
|
+
need: write5,
|
|
13209
|
+
risk: "W1",
|
|
13210
|
+
gates: ["VER"],
|
|
13211
|
+
run: async (ctx, { id }) => ws(ctx).stopAdVideo(Number(id))
|
|
13212
|
+
}),
|
|
12947
13213
|
tool({
|
|
12948
13214
|
name: "social_youtube_set",
|
|
12949
13215
|
description: "Change a YouTube post that hasn't gone out: title (at most 100 characters, aim 40-60), tags, visibility (public, unlisted, private), made_for_kids (YouTube asks for every video: ask the user, never guess) or synthetic (realistic AI-made or altered content). The description is the post's text (social_update_copy). Preview first.",
|
|
@@ -13253,8 +13519,8 @@ var CommanderError = class extends Error {
|
|
|
13253
13519
|
* @param {string} code an id string representing the error
|
|
13254
13520
|
* @param {string} message human-readable description of the error
|
|
13255
13521
|
*/
|
|
13256
|
-
constructor(exitCode2, code,
|
|
13257
|
-
super(
|
|
13522
|
+
constructor(exitCode2, code, message2) {
|
|
13523
|
+
super(message2);
|
|
13258
13524
|
Error.captureStackTrace(this, this.constructor);
|
|
13259
13525
|
this.name = this.constructor.name;
|
|
13260
13526
|
this.code = code;
|
|
@@ -13267,8 +13533,8 @@ var InvalidArgumentError = class extends CommanderError {
|
|
|
13267
13533
|
* Constructs the InvalidArgumentError class
|
|
13268
13534
|
* @param {string} [message] explanation of why argument is invalid
|
|
13269
13535
|
*/
|
|
13270
|
-
constructor(
|
|
13271
|
-
super(1, "commander.invalidArgument",
|
|
13536
|
+
constructor(message2) {
|
|
13537
|
+
super(1, "commander.invalidArgument", message2);
|
|
13272
13538
|
Error.captureStackTrace(this, this.constructor);
|
|
13273
13539
|
this.name = this.constructor.name;
|
|
13274
13540
|
}
|
|
@@ -13724,9 +13990,9 @@ var Help = class {
|
|
|
13724
13990
|
* @param {Help} helper
|
|
13725
13991
|
* @returns string[]
|
|
13726
13992
|
*/
|
|
13727
|
-
formatItemList(heading,
|
|
13728
|
-
if (
|
|
13729
|
-
return [helper.styleTitle(heading), ...
|
|
13993
|
+
formatItemList(heading, items2, helper) {
|
|
13994
|
+
if (items2.length === 0) return [];
|
|
13995
|
+
return [helper.styleTitle(heading), ...items2, ""];
|
|
13730
13996
|
}
|
|
13731
13997
|
/**
|
|
13732
13998
|
* Group items by their help group heading.
|
|
@@ -14814,9 +15080,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
14814
15080
|
* @return never
|
|
14815
15081
|
* @private
|
|
14816
15082
|
*/
|
|
14817
|
-
_exit(exitCode2, code,
|
|
15083
|
+
_exit(exitCode2, code, message2) {
|
|
14818
15084
|
if (this._exitCallback) {
|
|
14819
|
-
this._exitCallback(new CommanderError(exitCode2, code,
|
|
15085
|
+
this._exitCallback(new CommanderError(exitCode2, code, message2));
|
|
14820
15086
|
}
|
|
14821
15087
|
process2.exit(exitCode2);
|
|
14822
15088
|
}
|
|
@@ -14876,8 +15142,8 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
14876
15142
|
return target.parseArg(value, previous);
|
|
14877
15143
|
} catch (err) {
|
|
14878
15144
|
if (err.code === "commander.invalidArgument") {
|
|
14879
|
-
const
|
|
14880
|
-
this.error(
|
|
15145
|
+
const message2 = `${invalidArgumentMessage} ${err.message}`;
|
|
15146
|
+
this.error(message2, { exitCode: err.exitCode, code: err.code });
|
|
14881
15147
|
}
|
|
14882
15148
|
throw err;
|
|
14883
15149
|
}
|
|
@@ -15955,9 +16221,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
15955
16221
|
* @param {string} [errorOptions.code] - an id string representing the error
|
|
15956
16222
|
* @param {number} [errorOptions.exitCode] - used with process.exit
|
|
15957
16223
|
*/
|
|
15958
|
-
error(
|
|
16224
|
+
error(message2, errorOptions) {
|
|
15959
16225
|
this._outputConfiguration.outputError(
|
|
15960
|
-
`${
|
|
16226
|
+
`${message2}
|
|
15961
16227
|
`,
|
|
15962
16228
|
this._outputConfiguration.writeErr
|
|
15963
16229
|
);
|
|
@@ -15971,7 +16237,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
15971
16237
|
const config2 = errorOptions || {};
|
|
15972
16238
|
const exitCode2 = config2.exitCode || 1;
|
|
15973
16239
|
const code = config2.code || "commander.error";
|
|
15974
|
-
this._exit(exitCode2, code,
|
|
16240
|
+
this._exit(exitCode2, code, message2);
|
|
15975
16241
|
}
|
|
15976
16242
|
/**
|
|
15977
16243
|
* Apply any option related environment variables, if option does
|
|
@@ -16027,8 +16293,8 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
16027
16293
|
* @private
|
|
16028
16294
|
*/
|
|
16029
16295
|
missingArgument(name) {
|
|
16030
|
-
const
|
|
16031
|
-
this.error(
|
|
16296
|
+
const message2 = `error: missing required argument '${name}'`;
|
|
16297
|
+
this.error(message2, { code: "commander.missingArgument" });
|
|
16032
16298
|
}
|
|
16033
16299
|
/**
|
|
16034
16300
|
* `Option` is missing an argument.
|
|
@@ -16037,8 +16303,8 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
16037
16303
|
* @private
|
|
16038
16304
|
*/
|
|
16039
16305
|
optionMissingArgument(option) {
|
|
16040
|
-
const
|
|
16041
|
-
this.error(
|
|
16306
|
+
const message2 = `error: option '${option.flags}' argument missing`;
|
|
16307
|
+
this.error(message2, { code: "commander.optionMissingArgument" });
|
|
16042
16308
|
}
|
|
16043
16309
|
/**
|
|
16044
16310
|
* `Option` does not have a value, and is a mandatory option.
|
|
@@ -16047,8 +16313,8 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
16047
16313
|
* @private
|
|
16048
16314
|
*/
|
|
16049
16315
|
missingMandatoryOptionValue(option) {
|
|
16050
|
-
const
|
|
16051
|
-
this.error(
|
|
16316
|
+
const message2 = `error: required option '${option.flags}' not specified`;
|
|
16317
|
+
this.error(message2, { code: "commander.missingMandatoryOptionValue" });
|
|
16052
16318
|
}
|
|
16053
16319
|
/**
|
|
16054
16320
|
* `Option` conflicts with another option.
|
|
@@ -16081,8 +16347,8 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
16081
16347
|
}
|
|
16082
16348
|
return `option '${bestOption.flags}'`;
|
|
16083
16349
|
};
|
|
16084
|
-
const
|
|
16085
|
-
this.error(
|
|
16350
|
+
const message2 = `error: ${getErrorMessage(option)} cannot be used with ${getErrorMessage(conflictingOption)}`;
|
|
16351
|
+
this.error(message2, { code: "commander.conflictingOption" });
|
|
16086
16352
|
}
|
|
16087
16353
|
/**
|
|
16088
16354
|
* Unknown option `flag`.
|
|
@@ -16103,8 +16369,8 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
16103
16369
|
} while (command && !command._enablePositionalOptions);
|
|
16104
16370
|
suggestion = suggestSimilar(flag, candidateFlags);
|
|
16105
16371
|
}
|
|
16106
|
-
const
|
|
16107
|
-
this.error(
|
|
16372
|
+
const message2 = `error: unknown option '${flag}'${suggestion}`;
|
|
16373
|
+
this.error(message2, { code: "commander.unknownOption" });
|
|
16108
16374
|
}
|
|
16109
16375
|
/**
|
|
16110
16376
|
* Excess arguments, more than expected.
|
|
@@ -16119,8 +16385,8 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
16119
16385
|
const received = receivedArgs.length;
|
|
16120
16386
|
const forSubcommand = this.parent ? ` for '${this.name()}'` : "";
|
|
16121
16387
|
const details2 = receivedArgs.join(", ");
|
|
16122
|
-
const
|
|
16123
|
-
this.error(
|
|
16388
|
+
const message2 = `error: too many arguments${forSubcommand}. Expected ${expected} argument${s} but got ${received}: ${details2}.`;
|
|
16389
|
+
this.error(message2, { code: "commander.excessArguments" });
|
|
16124
16390
|
}
|
|
16125
16391
|
/**
|
|
16126
16392
|
* Unknown command.
|
|
@@ -16138,8 +16404,8 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
16138
16404
|
});
|
|
16139
16405
|
suggestion = suggestSimilar(unknownName, candidateNames);
|
|
16140
16406
|
}
|
|
16141
|
-
const
|
|
16142
|
-
this.error(
|
|
16407
|
+
const message2 = `error: unknown command '${unknownName}'${suggestion}`;
|
|
16408
|
+
this.error(message2, { code: "commander.unknownCommand" });
|
|
16143
16409
|
}
|
|
16144
16410
|
/**
|
|
16145
16411
|
* Get or set the program version.
|
|
@@ -16616,7 +16882,7 @@ var program = new Command();
|
|
|
16616
16882
|
// package.json
|
|
16617
16883
|
var package_default = {
|
|
16618
16884
|
name: "@cmo-so/cli",
|
|
16619
|
-
version: "0.
|
|
16885
|
+
version: "0.10.0",
|
|
16620
16886
|
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.",
|
|
16621
16887
|
type: "module",
|
|
16622
16888
|
license: "MIT",
|
|
@@ -16814,8 +17080,8 @@ function print(value, json, human) {
|
|
|
16814
17080
|
`);
|
|
16815
17081
|
return;
|
|
16816
17082
|
}
|
|
16817
|
-
const
|
|
16818
|
-
const out = human ? human(value) :
|
|
17083
|
+
const items2 = Array.isArray(value) ? value : isObj(value) && Array.isArray(value.items) ? value.items : null;
|
|
17084
|
+
const out = human ? human(value) : items2 && items2.every(isObj) ? table(items2) : details(value);
|
|
16819
17085
|
process.stdout.write(`${out}
|
|
16820
17086
|
`);
|
|
16821
17087
|
}
|
|
@@ -17414,8 +17680,8 @@ function prefixIssues(path2, issues) {
|
|
|
17414
17680
|
return iss;
|
|
17415
17681
|
});
|
|
17416
17682
|
}
|
|
17417
|
-
function unwrapMessage(
|
|
17418
|
-
return typeof
|
|
17683
|
+
function unwrapMessage(message2) {
|
|
17684
|
+
return typeof message2 === "string" ? message2 : message2?.message;
|
|
17419
17685
|
}
|
|
17420
17686
|
function sizeCaps(defChecks) {
|
|
17421
17687
|
const caps = [];
|
|
@@ -17445,7 +17711,7 @@ function finalizeIssue(iss, ctx, config2) {
|
|
|
17445
17711
|
iss.schema = iss.inst;
|
|
17446
17712
|
}
|
|
17447
17713
|
const schemaError = iss.schema !== iss.inst ? iss.schema?._zod.def?.error : void 0;
|
|
17448
|
-
const
|
|
17714
|
+
const message2 = iss.message ? iss.message : unwrapMessage(iss.inst?._zod.def?.error?.(iss)) ?? unwrapMessage(schemaError?.(iss)) ?? unwrapMessage(ctx?.error?.(iss)) ?? unwrapMessage(config2.customError?.(iss)) ?? unwrapMessage(config2.localeError?.(iss)) ?? "Invalid input";
|
|
17449
17715
|
const full = {};
|
|
17450
17716
|
for (const k of Object.keys(iss)) {
|
|
17451
17717
|
if (k === "inst" || k === "schema" || k === "continue" || k === "input" || k === "__proto__")
|
|
@@ -17453,7 +17719,7 @@ function finalizeIssue(iss, ctx, config2) {
|
|
|
17453
17719
|
full[k] = iss[k];
|
|
17454
17720
|
}
|
|
17455
17721
|
full.path ?? (full.path = []);
|
|
17456
|
-
full.message =
|
|
17722
|
+
full.message = message2;
|
|
17457
17723
|
if (ctx?.reportInput) {
|
|
17458
17724
|
full.input = iss.input;
|
|
17459
17725
|
}
|
|
@@ -19069,10 +19335,10 @@ function capSize(inst, size) {
|
|
|
19069
19335
|
const cap = Math.min(...caps.map((c) => c[0]));
|
|
19070
19336
|
const parse3 = inst._zod.parse;
|
|
19071
19337
|
inst._zod.parse = (payload, ctx) => {
|
|
19072
|
-
const
|
|
19073
|
-
if (
|
|
19338
|
+
const n2 = size(payload.value);
|
|
19339
|
+
if (n2 > cap && !ctx?.skipChecks && ctx?.direction !== "backward") {
|
|
19074
19340
|
for (const [max, ch] of sizeCaps(inst._zod.def.checks)) {
|
|
19075
|
-
if (
|
|
19341
|
+
if (n2 > max && ch._zod.def.when(payload)) {
|
|
19076
19342
|
const start = payload.issues.length;
|
|
19077
19343
|
ch._zod.check(payload);
|
|
19078
19344
|
payload.issues[start].continue = false;
|
|
@@ -21168,12 +21434,12 @@ function initializeContext(params) {
|
|
|
21168
21434
|
external: params?.external ?? void 0
|
|
21169
21435
|
};
|
|
21170
21436
|
}
|
|
21171
|
-
function handleUnrepresentable(schema, ctx, json, params,
|
|
21172
|
-
const result = typeof ctx.unrepresentable === "function" ? ctx.unrepresentable({ zodSchema: schema, path: params.path, message }) : ctx.unrepresentable;
|
|
21437
|
+
function handleUnrepresentable(schema, ctx, json, params, message2) {
|
|
21438
|
+
const result = typeof ctx.unrepresentable === "function" ? ctx.unrepresentable({ zodSchema: schema, path: params.path, message: message2 }) : ctx.unrepresentable;
|
|
21173
21439
|
if (result === "any")
|
|
21174
21440
|
return false;
|
|
21175
21441
|
if (result === void 0 || result === "throw")
|
|
21176
|
-
throw new Error(
|
|
21442
|
+
throw new Error(message2);
|
|
21177
21443
|
Object.assign(json, result);
|
|
21178
21444
|
return true;
|
|
21179
21445
|
}
|
|
@@ -22866,17 +23132,17 @@ var ZodArray = /* @__PURE__ */ $constructor("ZodArray", (inst, def) => {
|
|
|
22866
23132
|
inst._zod.processJSONSchema = (ctx, json, params) => arrayProcessor(inst, ctx, json, params);
|
|
22867
23133
|
inst.element = def.element;
|
|
22868
23134
|
}, {
|
|
22869
|
-
min(
|
|
22870
|
-
return this.check(_minLength(
|
|
23135
|
+
min(n2, params) {
|
|
23136
|
+
return this.check(_minLength(n2, params));
|
|
22871
23137
|
},
|
|
22872
23138
|
nonempty(params) {
|
|
22873
23139
|
return this.check(_minLength(1, params));
|
|
22874
23140
|
},
|
|
22875
|
-
max(
|
|
22876
|
-
return this.check(_maxLength(
|
|
23141
|
+
max(n2, params) {
|
|
23142
|
+
return this.check(_maxLength(n2, params));
|
|
22877
23143
|
},
|
|
22878
|
-
length(
|
|
22879
|
-
return this.check(_length(
|
|
23144
|
+
length(n2, params) {
|
|
23145
|
+
return this.check(_length(n2, params));
|
|
22880
23146
|
},
|
|
22881
23147
|
unwrap() {
|
|
22882
23148
|
return this.element;
|
|
@@ -24780,8 +25046,8 @@ var ServerResultSchema = union([
|
|
|
24780
25046
|
CreateTaskResultSchema
|
|
24781
25047
|
]);
|
|
24782
25048
|
var McpError = class _McpError extends Error {
|
|
24783
|
-
constructor(code,
|
|
24784
|
-
super(`MCP error ${code}: ${
|
|
25049
|
+
constructor(code, message2, data) {
|
|
25050
|
+
super(`MCP error ${code}: ${message2}`);
|
|
24785
25051
|
this.code = code;
|
|
24786
25052
|
this.data = data;
|
|
24787
25053
|
this.name = "McpError";
|
|
@@ -24789,19 +25055,19 @@ var McpError = class _McpError extends Error {
|
|
|
24789
25055
|
/**
|
|
24790
25056
|
* Factory method to create the appropriate error type based on the error code and data
|
|
24791
25057
|
*/
|
|
24792
|
-
static fromError(code,
|
|
25058
|
+
static fromError(code, message2, data) {
|
|
24793
25059
|
if (code === ErrorCode.UrlElicitationRequired && data) {
|
|
24794
25060
|
const errorData = data;
|
|
24795
25061
|
if (errorData.elicitations) {
|
|
24796
|
-
return new UrlElicitationRequiredError(errorData.elicitations,
|
|
25062
|
+
return new UrlElicitationRequiredError(errorData.elicitations, message2);
|
|
24797
25063
|
}
|
|
24798
25064
|
}
|
|
24799
|
-
return new _McpError(code,
|
|
25065
|
+
return new _McpError(code, message2, data);
|
|
24800
25066
|
}
|
|
24801
25067
|
};
|
|
24802
25068
|
var UrlElicitationRequiredError = class extends McpError {
|
|
24803
|
-
constructor(elicitations,
|
|
24804
|
-
super(ErrorCode.UrlElicitationRequired,
|
|
25069
|
+
constructor(elicitations, message2 = `URL elicitation${elicitations.length > 1 ? "s" : ""} required`) {
|
|
25070
|
+
super(ErrorCode.UrlElicitationRequired, message2, {
|
|
24805
25071
|
elicitations
|
|
24806
25072
|
});
|
|
24807
25073
|
}
|
|
@@ -24887,15 +25153,15 @@ var Protocol = class {
|
|
|
24887
25153
|
let queuedMessage;
|
|
24888
25154
|
while (queuedMessage = await this._taskMessageQueue.dequeue(taskId, extra.sessionId)) {
|
|
24889
25155
|
if (queuedMessage.type === "response" || queuedMessage.type === "error") {
|
|
24890
|
-
const
|
|
24891
|
-
const requestId =
|
|
25156
|
+
const message2 = queuedMessage.message;
|
|
25157
|
+
const requestId = message2.id;
|
|
24892
25158
|
const resolver = this._requestResolvers.get(requestId);
|
|
24893
25159
|
if (resolver) {
|
|
24894
25160
|
this._requestResolvers.delete(requestId);
|
|
24895
25161
|
if (queuedMessage.type === "response") {
|
|
24896
|
-
resolver(
|
|
25162
|
+
resolver(message2);
|
|
24897
25163
|
} else {
|
|
24898
|
-
const errorMessage3 =
|
|
25164
|
+
const errorMessage3 = message2;
|
|
24899
25165
|
const error2 = new McpError(errorMessage3.error.code, errorMessage3.error.message, errorMessage3.error.data);
|
|
24900
25166
|
resolver(error2);
|
|
24901
25167
|
}
|
|
@@ -25034,16 +25300,16 @@ var Protocol = class {
|
|
|
25034
25300
|
this._onerror(error2);
|
|
25035
25301
|
};
|
|
25036
25302
|
const _onmessage = this._transport?.onmessage;
|
|
25037
|
-
this._transport.onmessage = (
|
|
25038
|
-
_onmessage?.(
|
|
25039
|
-
if (isJSONRPCResultResponse(
|
|
25040
|
-
this._onresponse(
|
|
25041
|
-
} else if (isJSONRPCRequest(
|
|
25042
|
-
this._onrequest(
|
|
25043
|
-
} else if (isJSONRPCNotification(
|
|
25044
|
-
this._onnotification(
|
|
25303
|
+
this._transport.onmessage = (message2, extra) => {
|
|
25304
|
+
_onmessage?.(message2, extra);
|
|
25305
|
+
if (isJSONRPCResultResponse(message2) || isJSONRPCErrorResponse(message2)) {
|
|
25306
|
+
this._onresponse(message2);
|
|
25307
|
+
} else if (isJSONRPCRequest(message2)) {
|
|
25308
|
+
this._onrequest(message2, extra);
|
|
25309
|
+
} else if (isJSONRPCNotification(message2)) {
|
|
25310
|
+
this._onnotification(message2);
|
|
25045
25311
|
} else {
|
|
25046
|
-
this._onerror(new Error(`Unknown message type: ${JSON.stringify(
|
|
25312
|
+
this._onerror(new Error(`Unknown message type: ${JSON.stringify(message2)}`));
|
|
25047
25313
|
}
|
|
25048
25314
|
};
|
|
25049
25315
|
await this._transport.start();
|
|
@@ -25653,12 +25919,12 @@ var Protocol = class {
|
|
|
25653
25919
|
* the error appropriately (e.g., by failing the task, logging, etc.). The Protocol layer
|
|
25654
25920
|
* simply propagates the error.
|
|
25655
25921
|
*/
|
|
25656
|
-
async _enqueueTaskMessage(taskId,
|
|
25922
|
+
async _enqueueTaskMessage(taskId, message2, sessionId) {
|
|
25657
25923
|
if (!this._taskStore || !this._taskMessageQueue) {
|
|
25658
25924
|
throw new Error("Cannot enqueue task message: taskStore and taskMessageQueue are not configured");
|
|
25659
25925
|
}
|
|
25660
25926
|
const maxQueueSize = this._options?.maxTaskQueueSize;
|
|
25661
|
-
await this._taskMessageQueue.enqueue(taskId,
|
|
25927
|
+
await this._taskMessageQueue.enqueue(taskId, message2, sessionId, maxQueueSize);
|
|
25662
25928
|
}
|
|
25663
25929
|
/**
|
|
25664
25930
|
* Clears the message queue for a task and rejects any pending request resolvers.
|
|
@@ -25668,9 +25934,9 @@ var Protocol = class {
|
|
|
25668
25934
|
async _clearTaskQueue(taskId, sessionId) {
|
|
25669
25935
|
if (this._taskMessageQueue) {
|
|
25670
25936
|
const messages = await this._taskMessageQueue.dequeueAll(taskId, sessionId);
|
|
25671
|
-
for (const
|
|
25672
|
-
if (
|
|
25673
|
-
const requestId =
|
|
25937
|
+
for (const message2 of messages) {
|
|
25938
|
+
if (message2.type === "request" && isJSONRPCRequest(message2.message)) {
|
|
25939
|
+
const requestId = message2.message.id;
|
|
25674
25940
|
const resolver = this._requestResolvers.get(requestId);
|
|
25675
25941
|
if (resolver) {
|
|
25676
25942
|
resolver(new McpError(ErrorCode.InternalError, "Task cancelled or completed"));
|
|
@@ -26483,6 +26749,75 @@ var Server = class extends Protocol {
|
|
|
26483
26749
|
}
|
|
26484
26750
|
};
|
|
26485
26751
|
|
|
26752
|
+
// ../../packages/mcp/src/messages.ts
|
|
26753
|
+
var UPPER = /* @__PURE__ */ new Set(["seo", "gsc", "gbp", "cta", "cmo", "dns", "ai", "api", "pdf", "url", "id"]);
|
|
26754
|
+
var n = (count, word, plural) => count === 1 ? `1 ${word}` : `${count ? count : "no"} ${plural ?? `${word}s`}`;
|
|
26755
|
+
function titleOf(name) {
|
|
26756
|
+
const words = name.split("_").map((w) => UPPER.has(w) ? w.toUpperCase() : w);
|
|
26757
|
+
const head = words[0] ?? "";
|
|
26758
|
+
return [head.charAt(0).toUpperCase() + head.slice(1), ...words.slice(1)].join(" ");
|
|
26759
|
+
}
|
|
26760
|
+
var LIST_KEYS = ["items", "results", "posts", "ideas", "workspaces", "rows"];
|
|
26761
|
+
function items(value) {
|
|
26762
|
+
if (Array.isArray(value)) return value;
|
|
26763
|
+
if (value && typeof value === "object") {
|
|
26764
|
+
for (const k of LIST_KEYS) {
|
|
26765
|
+
const v = value[k];
|
|
26766
|
+
if (Array.isArray(v)) return v;
|
|
26767
|
+
}
|
|
26768
|
+
}
|
|
26769
|
+
return null;
|
|
26770
|
+
}
|
|
26771
|
+
function label(x) {
|
|
26772
|
+
if (x && typeof x === "object") {
|
|
26773
|
+
const o = x;
|
|
26774
|
+
for (const k of ["title", "name", "domain", "label", "what", "text", "id"]) {
|
|
26775
|
+
if (o[k] !== void 0 && o[k] !== null && o[k] !== "") return String(o[k]);
|
|
26776
|
+
}
|
|
26777
|
+
return Object.entries(o).filter(([, v]) => typeof v !== "object").slice(0, 3).map(([k, v]) => `${k} ${v}`).join(", ");
|
|
26778
|
+
}
|
|
26779
|
+
return String(x);
|
|
26780
|
+
}
|
|
26781
|
+
function bodyLines(value) {
|
|
26782
|
+
const list2 = items(value);
|
|
26783
|
+
if (list2) {
|
|
26784
|
+
const out = list2.slice(0, 8).map((x) => `- ${label(x)}`.slice(0, 200));
|
|
26785
|
+
return list2.length > 8 ? [...out, `\u2026and ${list2.length - 8} more`] : out;
|
|
26786
|
+
}
|
|
26787
|
+
if (value && typeof value === "object") {
|
|
26788
|
+
return Object.entries(value).filter(([k, v]) => v !== null && v !== void 0 && v !== "" && typeof v !== "object" && k !== "next").slice(0, 8).map(([k, v]) => `${k.replace(/_/g, " ")}: ${v}`.slice(0, 200));
|
|
26789
|
+
}
|
|
26790
|
+
return value === null || value === void 0 || value === "" ? [] : [String(value).slice(0, 600)];
|
|
26791
|
+
}
|
|
26792
|
+
function said(name, value) {
|
|
26793
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
26794
|
+
for (const k of ["say", "headline", "summary", "message"]) {
|
|
26795
|
+
const v = value[k];
|
|
26796
|
+
if (typeof v === "string" && v.trim()) return (v.trim().split("\n")[0] ?? "").slice(0, 240);
|
|
26797
|
+
}
|
|
26798
|
+
}
|
|
26799
|
+
const list2 = items(value);
|
|
26800
|
+
if (list2) {
|
|
26801
|
+
let noun = name.endsWith("_list") ? name.slice(0, -5).split("_").pop() ?? "item" : "item";
|
|
26802
|
+
if (noun.endsWith("s")) noun = noun.slice(0, -1);
|
|
26803
|
+
return list2.length ? `I found ${n(list2.length, noun)}.` : "There's nothing there yet.";
|
|
26804
|
+
}
|
|
26805
|
+
if (typeof value === "string") return (value.trim().split("\n")[0] ?? "").slice(0, 240) || "Done.";
|
|
26806
|
+
return "Here it is.";
|
|
26807
|
+
}
|
|
26808
|
+
var lowerFirst = (s) => /^[A-Za-z][a-z]/.test(s) ? s.charAt(0).toLowerCase() + s.slice(1) : s;
|
|
26809
|
+
function message(name, value, opts = {}) {
|
|
26810
|
+
const title = titleOf(name);
|
|
26811
|
+
if (opts.error) return { say: `That didn't work: ${opts.error}`.slice(0, 300), title, lines: [] };
|
|
26812
|
+
if (opts.preview) return { say: "This needs your yes first; nothing has changed yet.", title, lines: [opts.preview] };
|
|
26813
|
+
if (opts.done) return { say: `Done: ${lowerFirst(opts.done)}`, title, lines: bodyLines(value) };
|
|
26814
|
+
return { say: said(name, value), title, lines: bodyLines(value) };
|
|
26815
|
+
}
|
|
26816
|
+
function messageText(m) {
|
|
26817
|
+
const body = [m.title ? `**${m.title}**` : "", ...m.lines].filter(Boolean).join("\n");
|
|
26818
|
+
return [m.say, body].filter(Boolean).join("\n\n").trim();
|
|
26819
|
+
}
|
|
26820
|
+
|
|
26486
26821
|
// ../../packages/mcp/src/prompts.ts
|
|
26487
26822
|
var workspaceArg = {
|
|
26488
26823
|
name: "workspace",
|
|
@@ -26835,14 +27170,14 @@ function toolArgs(args) {
|
|
|
26835
27170
|
return out;
|
|
26836
27171
|
}
|
|
26837
27172
|
function withIdempotency(api2, approvalId) {
|
|
26838
|
-
let
|
|
27173
|
+
let n2 = 0;
|
|
26839
27174
|
return {
|
|
26840
27175
|
...api2,
|
|
26841
27176
|
request: (path2, init = {}) => {
|
|
26842
27177
|
const method = (init.method ?? "GET").toUpperCase();
|
|
26843
27178
|
if (method === "GET" || method === "HEAD") return api2.request(path2, init);
|
|
26844
27179
|
const headers = new Headers(init.headers);
|
|
26845
|
-
headers.set("Idempotency-Key", idempotencyKey(approvalId, 0,
|
|
27180
|
+
headers.set("Idempotency-Key", idempotencyKey(approvalId, 0, n2++));
|
|
26846
27181
|
return api2.request(path2, { ...init, headers });
|
|
26847
27182
|
}
|
|
26848
27183
|
};
|
|
@@ -26850,13 +27185,18 @@ function withIdempotency(api2, approvalId) {
|
|
|
26850
27185
|
var text2 = (value) => [
|
|
26851
27186
|
{ type: "text", text: typeof value === "string" ? value : JSON.stringify(value) }
|
|
26852
27187
|
];
|
|
26853
|
-
function ok(result, extra) {
|
|
27188
|
+
function ok(result, extra, msg) {
|
|
26854
27189
|
const value = result ?? { ok: true };
|
|
26855
27190
|
const structured = value && typeof value === "object" && !Array.isArray(value) ? value : { items: value };
|
|
26856
|
-
return { content: text2(value), structuredContent: { ...structured, ...extra } };
|
|
27191
|
+
if (!msg) return { content: text2(value), structuredContent: { ...structured, ...extra } };
|
|
27192
|
+
return {
|
|
27193
|
+
content: [...text2(messageText(msg)), ...text2(value)],
|
|
27194
|
+
structuredContent: { ...structured, ...extra, message: msg }
|
|
27195
|
+
};
|
|
26857
27196
|
}
|
|
26858
|
-
function fail(
|
|
26859
|
-
return { content: text2({ error:
|
|
27197
|
+
function fail(error2, tool3) {
|
|
27198
|
+
if (!tool3) return { content: text2({ error: error2 }), isError: true };
|
|
27199
|
+
return { content: [...text2(messageText(message(tool3, void 0, { error: error2 }))), ...text2({ error: error2 })], isError: true };
|
|
26860
27200
|
}
|
|
26861
27201
|
function errorMessage2(e) {
|
|
26862
27202
|
if (e instanceof SignInRequiredError) return "The sign-in has expired. Reconnect CMO.so and try again.";
|
|
@@ -26996,7 +27336,10 @@ function createMcpServer(deps) {
|
|
|
26996
27336
|
);
|
|
26997
27337
|
const callArgs = approval ? withFreshFiles(t, approval.calls[0]?.args ?? {}, toolArgs(args)) : toolArgs(args);
|
|
26998
27338
|
const ctx = { api: api2, domain, client: "mcp" };
|
|
26999
|
-
if (!isWrite(t))
|
|
27339
|
+
if (!isWrite(t)) {
|
|
27340
|
+
const value = await t.run(ctx, callArgs);
|
|
27341
|
+
return ok(value, viewForTool(t.name) ? { workspace: domain } : void 0, message(t.name, value));
|
|
27342
|
+
}
|
|
27000
27343
|
let standing = null;
|
|
27001
27344
|
if (!approval && !deps.previewOnly && domain && isStanding(t.name)) {
|
|
27002
27345
|
const claim2 = await api2.account?.claimStanding({ website: domain, tool: t.name, units: standingUnits(t.name, callArgs) }).catch(() => null);
|
|
@@ -27018,14 +27361,19 @@ function createMcpServer(deps) {
|
|
|
27018
27361
|
const calls = [{ name: t.name, args: callArgs }];
|
|
27019
27362
|
const summary2 = t.confirm?.(callArgs, "en") ?? t.description;
|
|
27020
27363
|
const cost2 = await previewCost(api2, domain, t);
|
|
27021
|
-
|
|
27022
|
-
|
|
27023
|
-
|
|
27024
|
-
|
|
27025
|
-
|
|
27026
|
-
|
|
27027
|
-
|
|
27028
|
-
|
|
27364
|
+
const preview = cost2 ? `${summary2} ${cost2}` : summary2;
|
|
27365
|
+
return ok(
|
|
27366
|
+
{
|
|
27367
|
+
status: "needs_confirmation",
|
|
27368
|
+
preview,
|
|
27369
|
+
risk: t.risk,
|
|
27370
|
+
workspace: domain,
|
|
27371
|
+
confirmation: signer.sign({ calls, principal, domain }),
|
|
27372
|
+
next: "Show the preview to the user. If they agree, call this tool again with confirm: true and this confirmation (it runs exactly what was previewed). Nothing has changed yet."
|
|
27373
|
+
},
|
|
27374
|
+
void 0,
|
|
27375
|
+
message(t.name, void 0, { preview })
|
|
27376
|
+
);
|
|
27029
27377
|
}
|
|
27030
27378
|
const summary = `${t.confirm?.(callArgs, "en") ?? t.description}${standing ? ` (standing permission #${standing})` : ""}`;
|
|
27031
27379
|
try {
|
|
@@ -27033,14 +27381,14 @@ function createMcpServer(deps) {
|
|
|
27033
27381
|
const added = t.name === "website_add" ? result?.domain : null;
|
|
27034
27382
|
const where = typeof added === "string" && added ? added : domain;
|
|
27035
27383
|
await deps.audit?.({ domain: where, actions: [{ tool: t.name, summary, ok: true, action_id: approval.id }] }).catch(() => void 0);
|
|
27036
|
-
return ok(result);
|
|
27384
|
+
return ok(result, void 0, message(t.name, result, { done: summary }));
|
|
27037
27385
|
} catch (e) {
|
|
27038
27386
|
const error2 = errorMessage2(e);
|
|
27039
27387
|
await deps.audit?.({ domain, actions: [{ tool: t.name, summary, ok: false, error: error2, action_id: approval.id }] }).catch(() => void 0);
|
|
27040
|
-
return fail(error2);
|
|
27388
|
+
return fail(error2, t.name);
|
|
27041
27389
|
}
|
|
27042
27390
|
} catch (e) {
|
|
27043
|
-
return fail(errorMessage2(e));
|
|
27391
|
+
return fail(errorMessage2(e), t.name);
|
|
27044
27392
|
}
|
|
27045
27393
|
});
|
|
27046
27394
|
server.setRequestHandler(ListResourceTemplatesRequestSchema, async () => ({
|
|
@@ -27170,8 +27518,8 @@ var ReadBuffer = class {
|
|
|
27170
27518
|
function deserializeMessage(line) {
|
|
27171
27519
|
return JSONRPCMessageSchema.parse(JSON.parse(line));
|
|
27172
27520
|
}
|
|
27173
|
-
function serializeMessage(
|
|
27174
|
-
return JSON.stringify(
|
|
27521
|
+
function serializeMessage(message2) {
|
|
27522
|
+
return JSON.stringify(message2) + "\n";
|
|
27175
27523
|
}
|
|
27176
27524
|
|
|
27177
27525
|
// ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.30.1_zod@4.6.7/node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js
|
|
@@ -27209,11 +27557,11 @@ var StdioServerTransport = class {
|
|
|
27209
27557
|
processReadBuffer() {
|
|
27210
27558
|
while (true) {
|
|
27211
27559
|
try {
|
|
27212
|
-
const
|
|
27213
|
-
if (
|
|
27560
|
+
const message2 = this._readBuffer.readMessage();
|
|
27561
|
+
if (message2 === null) {
|
|
27214
27562
|
break;
|
|
27215
27563
|
}
|
|
27216
|
-
this.onmessage?.(
|
|
27564
|
+
this.onmessage?.(message2);
|
|
27217
27565
|
} catch (error2) {
|
|
27218
27566
|
this.onerror?.(error2);
|
|
27219
27567
|
}
|
|
@@ -27229,9 +27577,9 @@ var StdioServerTransport = class {
|
|
|
27229
27577
|
this._readBuffer.clear();
|
|
27230
27578
|
this.onclose?.();
|
|
27231
27579
|
}
|
|
27232
|
-
send(
|
|
27580
|
+
send(message2) {
|
|
27233
27581
|
return new Promise((resolve) => {
|
|
27234
|
-
const json = serializeMessage(
|
|
27582
|
+
const json = serializeMessage(message2);
|
|
27235
27583
|
if (this._stdout.write(json)) {
|
|
27236
27584
|
resolve();
|
|
27237
27585
|
} else {
|
|
@@ -27259,8 +27607,8 @@ async function serveStdio(version2) {
|
|
|
27259
27607
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
27260
27608
|
import { createInterface } from "node:readline/promises";
|
|
27261
27609
|
var CliError = class extends Error {
|
|
27262
|
-
constructor(
|
|
27263
|
-
super(
|
|
27610
|
+
constructor(message2, code = 2) {
|
|
27611
|
+
super(message2);
|
|
27264
27612
|
this.code = code;
|
|
27265
27613
|
}
|
|
27266
27614
|
};
|
|
@@ -27315,14 +27663,14 @@ async function cost(api2, domain, t) {
|
|
|
27315
27663
|
}
|
|
27316
27664
|
function withIdempotency2(api2) {
|
|
27317
27665
|
const base = randomUUID3();
|
|
27318
|
-
let
|
|
27666
|
+
let n2 = 0;
|
|
27319
27667
|
return {
|
|
27320
27668
|
...api2,
|
|
27321
27669
|
request: (path2, init = {}) => {
|
|
27322
27670
|
const method = (init.method ?? "GET").toUpperCase();
|
|
27323
27671
|
if (method === "GET" || method === "HEAD") return api2.request(path2, init);
|
|
27324
27672
|
const headers = new Headers(init.headers);
|
|
27325
|
-
headers.set("Idempotency-Key", `${base}-${
|
|
27673
|
+
headers.set("Idempotency-Key", `${base}-${n2++}`);
|
|
27326
27674
|
return api2.request(path2, { ...init, headers });
|
|
27327
27675
|
}
|
|
27328
27676
|
};
|
|
@@ -27388,9 +27736,9 @@ async function tool2(opts, name, args = {}, human) {
|
|
|
27388
27736
|
print(out.result, opts.json, human);
|
|
27389
27737
|
}
|
|
27390
27738
|
var ids2 = (list2) => list2.map((x) => {
|
|
27391
|
-
const
|
|
27392
|
-
if (!Number.isInteger(
|
|
27393
|
-
return
|
|
27739
|
+
const n2 = Number(x);
|
|
27740
|
+
if (!Number.isInteger(n2)) throw new CliError(`Not an id: ${x}`);
|
|
27741
|
+
return n2;
|
|
27394
27742
|
});
|
|
27395
27743
|
var program2 = new Command("cmo").description("CMO.so on the command line. Docs: https://ai.cmo.so/hot/mcp").version(VERSION2).option("--json", "machine-readable output").option("-w, --workspace <domain>", "the website to act on").option("-y, --yes", "don't ask before changing things (for scripts and CI)").option("--dry-run", "show what a change would do, without doing it").showHelpAfterError();
|
|
27396
27744
|
program2.command("login").description("sign in in your browser (device code)").option("--no-browser", "only print the link").action(
|
|
@@ -27626,7 +27974,18 @@ social.command("pictures <id> <urls...>").description("your own pictures on a po
|
|
|
27626
27974
|
async (opts, id, urls) => tool2(opts, "social_pictures_attach", { id: ids2([id])[0], urls, append: opts.append === true })
|
|
27627
27975
|
)
|
|
27628
27976
|
);
|
|
27629
|
-
social.command("
|
|
27977
|
+
social.command("slides <id> [count]").description("a text carousel of 5-10 slides for a post (a cover, one point a slide, the next step; free)").option("--style <style>", "bold_type (big words, the default) or infographic (lists and numbers)").action(
|
|
27978
|
+
act(
|
|
27979
|
+
async (opts, id, count) => tool2(opts, "social_image_new", {
|
|
27980
|
+
id: ids2([id])[0],
|
|
27981
|
+
count: Number(count ?? 7),
|
|
27982
|
+
...opts.style ? { style: opts.style } : {}
|
|
27983
|
+
})
|
|
27984
|
+
)
|
|
27985
|
+
);
|
|
27986
|
+
social.command("carousel <id>").description(
|
|
27987
|
+
"a post's carousel: --pdf for LinkedIn, Instagram, Facebook or TikTok; --format / --title for LinkedIn's"
|
|
27988
|
+
).option("--format <format>", "pdf (a swipeable carousel, the default) or images").option("--title <text>", "shown on the carousel").option("--pdf <url>", "a public https link to your own PDF: its pages become the pictures (LinkedIn: the PDF)").action(
|
|
27630
27989
|
act(async (opts, id) => {
|
|
27631
27990
|
const post = ids2([id])[0];
|
|
27632
27991
|
if (opts.pdf)
|
|
@@ -27718,6 +28077,58 @@ video.command("add <url>").description("posts from a direct link to a video file
|
|
|
27718
28077
|
})
|
|
27719
28078
|
)
|
|
27720
28079
|
);
|
|
28080
|
+
video.command("post <videoId>").description("posts from a video already in CMO.so (e.g. a ready ad video's video_id), one per channel").option("--channel <names>", "linkedin, facebook, instagram, youtube, tiktok (comma-separated)", "youtube").option("--note <text>", "what the video is about").action(
|
|
28081
|
+
act(
|
|
28082
|
+
async (opts, videoId) => tool2(opts, "social_video_add", {
|
|
28083
|
+
video_id: ids2([videoId])[0],
|
|
28084
|
+
channels: (channelsOpt(opts.channel) ?? ["youtube"]).filter((c) => c !== "google_business"),
|
|
28085
|
+
...opts.note ? { note: opts.note } : {}
|
|
28086
|
+
})
|
|
28087
|
+
)
|
|
28088
|
+
);
|
|
28089
|
+
var ad = video.command("ad").description("ad videos (beta): plan, change, make (billed), post");
|
|
28090
|
+
ad.command("plan [brief...]").description("a storyboard and what making it costs (one AI generation)").option("--seconds <n>", "15, 20 or 30", "20").option("--landscape", "16:9 instead of vertical 9:16").option("--scenes <n>", "2-5").option("--from-post <id>", "make it from a post's text").option("--reference <youtubeUrl>", "a YouTube ad whose rhythm to follow").action(
|
|
28091
|
+
act(
|
|
28092
|
+
async (opts, brief2) => tool2(opts, "ad_video_plan", {
|
|
28093
|
+
...brief2?.length ? { brief: brief2.join(" ") } : {},
|
|
28094
|
+
...opts.fromPost ? { item_id: ids2([opts.fromPost])[0] } : {},
|
|
28095
|
+
seconds: Number(opts.seconds),
|
|
28096
|
+
aspect: opts.landscape ? "16:9" : "9:16",
|
|
28097
|
+
...opts.scenes ? { scenes: Number(opts.scenes) } : {},
|
|
28098
|
+
...opts.reference ? { reference_url: opts.reference } : {}
|
|
28099
|
+
})
|
|
28100
|
+
)
|
|
28101
|
+
);
|
|
28102
|
+
ad.command("list").description("this website's ad videos").action(act(async (opts) => tool2(opts, "ad_video_list")));
|
|
28103
|
+
ad.command("show <id>").description("one ad video: storyboard, cost, status").action(act(async (opts, id) => tool2(opts, "ad_video_get", { id: ids2([id])[0] })));
|
|
28104
|
+
ad.command("change <id>").description("change the storyboard (only what a change touches is made again)").option("--scene <id=field:text...>", "e.g. s2=line:Book today (fields: still, motion, line, caption, seconds)").option("--redo <sceneIds>", "make these scenes' pictures and clips again (comma-separated)").option("--headline <text>", "the end card's headline").option("--sub <text>", "the end card's line").option("--voice <name>", "a voice name, or none").option("--music <text>", "the music in words, or none").action(
|
|
28105
|
+
act(
|
|
28106
|
+
async (opts, id) => {
|
|
28107
|
+
const scenes = {};
|
|
28108
|
+
for (const spec of opts.scene ?? []) {
|
|
28109
|
+
const m = /^([a-z0-9]+)=(still|motion|line|caption|seconds):(.*)$/s.exec(spec);
|
|
28110
|
+
if (!m) throw new Error(`--scene ${spec}: use <sceneId>=<field>:<text>, e.g. s2=line:Book today`);
|
|
28111
|
+
const [, sid = "", field = "", text3 = ""] = m;
|
|
28112
|
+
scenes[sid] = { ...scenes[sid] ?? {}, [field]: field === "seconds" ? Number(text3) : text3 };
|
|
28113
|
+
}
|
|
28114
|
+
return tool2(opts, "ad_video_change", {
|
|
28115
|
+
id: ids2([id])[0],
|
|
28116
|
+
...Object.keys(scenes).length ? { scenes: Object.entries(scenes).map(([sid, f]) => ({ id: sid, ...f })) } : {},
|
|
28117
|
+
...opts.redo ? { redo_scenes: opts.redo.split(",").map((x) => x.trim()) } : {},
|
|
28118
|
+
...opts.headline ? { cta_headline: opts.headline } : {},
|
|
28119
|
+
...opts.sub ? { cta_sub: opts.sub } : {},
|
|
28120
|
+
...opts.voice ? { voice: opts.voice } : {},
|
|
28121
|
+
...opts.music ? { music: opts.music } : {}
|
|
28122
|
+
});
|
|
28123
|
+
}
|
|
28124
|
+
)
|
|
28125
|
+
);
|
|
28126
|
+
ad.command("make <id> <usd>").description("make it: costs money; <usd> is the estimate you agreed to (from plan / show)").action(
|
|
28127
|
+
act(
|
|
28128
|
+
async (opts, id, usd) => tool2(opts, "ad_video_make", { id: ids2([id])[0], estimate_usd: Number(usd) })
|
|
28129
|
+
)
|
|
28130
|
+
);
|
|
28131
|
+
ad.command("stop <id>").description("stop one being made (what's made is kept), or remove one").action(act(async (opts, id) => tool2(opts, "ad_video_stop", { id: ids2([id])[0] })));
|
|
27721
28132
|
video.command("attach <id> <url>").description("make a LinkedIn / Facebook / Instagram post a video post (its picture is removed)").action(act(async (opts, id, url) => tool2(opts, "social_video_attach", { id: ids2([id])[0], url })));
|
|
27722
28133
|
video.command("detach <id>").description("take the video off a post (it's a text post again)").action(act(async (opts, id) => tool2(opts, "social_video_detach", { id: ids2([id])[0] })));
|
|
27723
28134
|
var tiktok = social.command("tiktok").description("your TikTok account (beta): short videos you approve one by one");
|
|
@@ -27737,6 +28148,15 @@ tiktok.command("add <url>").description("a TikTok post from a direct link to a v
|
|
|
27737
28148
|
})
|
|
27738
28149
|
)
|
|
27739
28150
|
);
|
|
28151
|
+
tiktok.command("photo <id>").description("a TikTok photo post from a post's pictures (a set, a text carousel or a PDF's pages)").option("--note <text>", "what to say in the caption").option("--caption <text>", "your own caption (no AI)").action(
|
|
28152
|
+
act(
|
|
28153
|
+
async (opts, id) => tool2(opts, "social_tiktok_photo", {
|
|
28154
|
+
id: ids2([id])[0],
|
|
28155
|
+
...opts.note ? { note: opts.note } : {},
|
|
28156
|
+
...opts.caption ? { caption: opts.caption } : {}
|
|
28157
|
+
})
|
|
28158
|
+
)
|
|
28159
|
+
);
|
|
27740
28160
|
tiktok.command("set <id>").description("a TikTok post's settings, as you choose them (nothing is preset)").option("--privacy <p>", "everyone, friends, followers or only-me (what your account offers)").option("--comment <yes|no>").option("--duet <yes|no>").option("--stitch <yes|no>").option("--promotes <what>", "none, your-brand, branded or both (content disclosure)").option("--ai <yes|no>", "AI-generated content label").action(
|
|
27741
28161
|
act(
|
|
27742
28162
|
async (opts, id) => {
|
|
@@ -27860,12 +28280,12 @@ feedback.command("send <summary...>", { isDefault: true }).description("send fee
|
|
|
27860
28280
|
);
|
|
27861
28281
|
feedback.command("ls").description("what you sent, its status and the team's reply").action(
|
|
27862
28282
|
act(async (opts) => {
|
|
27863
|
-
const { items } = await api().feedback.mine();
|
|
28283
|
+
const { items: items2 } = await api().feedback.mine();
|
|
27864
28284
|
print(
|
|
27865
|
-
|
|
28285
|
+
items2,
|
|
27866
28286
|
opts.json,
|
|
27867
|
-
() =>
|
|
27868
|
-
|
|
28287
|
+
() => items2.length ? table(
|
|
28288
|
+
items2.map((f) => ({
|
|
27869
28289
|
sent: f.created_at.slice(0, 10),
|
|
27870
28290
|
type: f.type,
|
|
27871
28291
|
status: f.status.replace("_", " ") + (f.unread ? " (new reply)" : ""),
|