@zanfia/cli 0.13.1 → 0.21.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/index.js +1875 -303
- package/package.json +38 -38
package/dist/index.js
CHANGED
|
@@ -33,9 +33,9 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
33
33
|
mod
|
|
34
34
|
));
|
|
35
35
|
|
|
36
|
-
//
|
|
36
|
+
// node_modules/commander/lib/error.js
|
|
37
37
|
var require_error = __commonJS({
|
|
38
|
-
"
|
|
38
|
+
"node_modules/commander/lib/error.js"(exports) {
|
|
39
39
|
"use strict";
|
|
40
40
|
var CommanderError2 = class extends Error {
|
|
41
41
|
/**
|
|
@@ -69,9 +69,9 @@ var require_error = __commonJS({
|
|
|
69
69
|
}
|
|
70
70
|
});
|
|
71
71
|
|
|
72
|
-
//
|
|
72
|
+
// node_modules/commander/lib/argument.js
|
|
73
73
|
var require_argument = __commonJS({
|
|
74
|
-
"
|
|
74
|
+
"node_modules/commander/lib/argument.js"(exports) {
|
|
75
75
|
"use strict";
|
|
76
76
|
var { InvalidArgumentError: InvalidArgumentError2 } = require_error();
|
|
77
77
|
var Argument2 = class {
|
|
@@ -197,9 +197,9 @@ var require_argument = __commonJS({
|
|
|
197
197
|
}
|
|
198
198
|
});
|
|
199
199
|
|
|
200
|
-
//
|
|
200
|
+
// node_modules/commander/lib/help.js
|
|
201
201
|
var require_help = __commonJS({
|
|
202
|
-
"
|
|
202
|
+
"node_modules/commander/lib/help.js"(exports) {
|
|
203
203
|
"use strict";
|
|
204
204
|
var { humanReadableArgName } = require_argument();
|
|
205
205
|
var Help2 = class {
|
|
@@ -612,9 +612,9 @@ var require_help = __commonJS({
|
|
|
612
612
|
}
|
|
613
613
|
});
|
|
614
614
|
|
|
615
|
-
//
|
|
615
|
+
// node_modules/commander/lib/option.js
|
|
616
616
|
var require_option = __commonJS({
|
|
617
|
-
"
|
|
617
|
+
"node_modules/commander/lib/option.js"(exports) {
|
|
618
618
|
"use strict";
|
|
619
619
|
var { InvalidArgumentError: InvalidArgumentError2 } = require_error();
|
|
620
620
|
var Option2 = class {
|
|
@@ -885,9 +885,9 @@ var require_option = __commonJS({
|
|
|
885
885
|
}
|
|
886
886
|
});
|
|
887
887
|
|
|
888
|
-
//
|
|
888
|
+
// node_modules/commander/lib/suggestSimilar.js
|
|
889
889
|
var require_suggestSimilar = __commonJS({
|
|
890
|
-
"
|
|
890
|
+
"node_modules/commander/lib/suggestSimilar.js"(exports) {
|
|
891
891
|
"use strict";
|
|
892
892
|
var maxDistance = 3;
|
|
893
893
|
function editDistance(a, b) {
|
|
@@ -966,9 +966,9 @@ var require_suggestSimilar = __commonJS({
|
|
|
966
966
|
}
|
|
967
967
|
});
|
|
968
968
|
|
|
969
|
-
//
|
|
969
|
+
// node_modules/commander/lib/command.js
|
|
970
970
|
var require_command = __commonJS({
|
|
971
|
-
"
|
|
971
|
+
"node_modules/commander/lib/command.js"(exports) {
|
|
972
972
|
"use strict";
|
|
973
973
|
var EventEmitter = __require("events").EventEmitter;
|
|
974
974
|
var childProcess = __require("child_process");
|
|
@@ -1024,7 +1024,7 @@ var require_command = __commonJS({
|
|
|
1024
1024
|
writeErr: (str) => process2.stderr.write(str),
|
|
1025
1025
|
getOutHelpWidth: () => process2.stdout.isTTY ? process2.stdout.columns : void 0,
|
|
1026
1026
|
getErrHelpWidth: () => process2.stderr.isTTY ? process2.stderr.columns : void 0,
|
|
1027
|
-
outputError: (str,
|
|
1027
|
+
outputError: (str, write2) => write2(str)
|
|
1028
1028
|
};
|
|
1029
1029
|
this._hidden = false;
|
|
1030
1030
|
this._helpOption = void 0;
|
|
@@ -2824,13 +2824,13 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2824
2824
|
_getHelpContext(contextOptions) {
|
|
2825
2825
|
contextOptions = contextOptions || {};
|
|
2826
2826
|
const context = { error: !!contextOptions.error };
|
|
2827
|
-
let
|
|
2827
|
+
let write2;
|
|
2828
2828
|
if (context.error) {
|
|
2829
|
-
|
|
2829
|
+
write2 = (arg) => this._outputConfiguration.writeErr(arg);
|
|
2830
2830
|
} else {
|
|
2831
|
-
|
|
2831
|
+
write2 = (arg) => this._outputConfiguration.writeOut(arg);
|
|
2832
2832
|
}
|
|
2833
|
-
context.write = contextOptions.write ||
|
|
2833
|
+
context.write = contextOptions.write || write2;
|
|
2834
2834
|
context.command = this;
|
|
2835
2835
|
return context;
|
|
2836
2836
|
}
|
|
@@ -3010,9 +3010,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
3010
3010
|
}
|
|
3011
3011
|
});
|
|
3012
3012
|
|
|
3013
|
-
//
|
|
3013
|
+
// node_modules/commander/index.js
|
|
3014
3014
|
var require_commander = __commonJS({
|
|
3015
|
-
"
|
|
3015
|
+
"node_modules/commander/index.js"(exports) {
|
|
3016
3016
|
"use strict";
|
|
3017
3017
|
var { Argument: Argument2 } = require_argument();
|
|
3018
3018
|
var { Command: Command4 } = require_command();
|
|
@@ -3033,9 +3033,9 @@ var require_commander = __commonJS({
|
|
|
3033
3033
|
}
|
|
3034
3034
|
});
|
|
3035
3035
|
|
|
3036
|
-
//
|
|
3036
|
+
// ../../node_modules/requires-port/index.js
|
|
3037
3037
|
var require_requires_port = __commonJS({
|
|
3038
|
-
"
|
|
3038
|
+
"../../node_modules/requires-port/index.js"(exports, module) {
|
|
3039
3039
|
"use strict";
|
|
3040
3040
|
module.exports = function required(port, protocol) {
|
|
3041
3041
|
protocol = protocol.split(":")[0];
|
|
@@ -3060,9 +3060,9 @@ var require_requires_port = __commonJS({
|
|
|
3060
3060
|
}
|
|
3061
3061
|
});
|
|
3062
3062
|
|
|
3063
|
-
//
|
|
3063
|
+
// ../../node_modules/querystringify/index.js
|
|
3064
3064
|
var require_querystringify = __commonJS({
|
|
3065
|
-
"
|
|
3065
|
+
"../../node_modules/querystringify/index.js"(exports) {
|
|
3066
3066
|
"use strict";
|
|
3067
3067
|
var has = Object.prototype.hasOwnProperty;
|
|
3068
3068
|
var undef;
|
|
@@ -3112,9 +3112,9 @@ var require_querystringify = __commonJS({
|
|
|
3112
3112
|
}
|
|
3113
3113
|
});
|
|
3114
3114
|
|
|
3115
|
-
//
|
|
3115
|
+
// ../../node_modules/url-parse/index.js
|
|
3116
3116
|
var require_url_parse = __commonJS({
|
|
3117
|
-
"
|
|
3117
|
+
"../../node_modules/url-parse/index.js"(exports, module) {
|
|
3118
3118
|
"use strict";
|
|
3119
3119
|
var required = require_requires_port();
|
|
3120
3120
|
var qs = require_querystringify();
|
|
@@ -3221,9 +3221,9 @@ var require_url_parse = __commonJS({
|
|
|
3221
3221
|
rest
|
|
3222
3222
|
};
|
|
3223
3223
|
}
|
|
3224
|
-
function
|
|
3225
|
-
if (
|
|
3226
|
-
var path2 = (base || "/").split("/").slice(0, -1).concat(
|
|
3224
|
+
function resolve3(relative2, base) {
|
|
3225
|
+
if (relative2 === "") return base;
|
|
3226
|
+
var path2 = (base || "/").split("/").slice(0, -1).concat(relative2.split("/")), i = path2.length, last = path2[i - 1], unshift = false, up = 0;
|
|
3227
3227
|
while (i--) {
|
|
3228
3228
|
if (path2[i] === ".") {
|
|
3229
3229
|
path2.splice(i, 1);
|
|
@@ -3246,7 +3246,7 @@ var require_url_parse = __commonJS({
|
|
|
3246
3246
|
if (!(this instanceof Url)) {
|
|
3247
3247
|
return new Url(address, location, parser);
|
|
3248
3248
|
}
|
|
3249
|
-
var
|
|
3249
|
+
var relative2, extracted, parse2, instruction, index, key, instructions = rules.slice(), type = typeof location, url = this, i = 0;
|
|
3250
3250
|
if ("object" !== type && "string" !== type) {
|
|
3251
3251
|
parser = location;
|
|
3252
3252
|
location = null;
|
|
@@ -3254,8 +3254,8 @@ var require_url_parse = __commonJS({
|
|
|
3254
3254
|
if (parser && "function" !== typeof parser) parser = qs.parse;
|
|
3255
3255
|
location = lolcation(location);
|
|
3256
3256
|
extracted = extractProtocol(address || "", location);
|
|
3257
|
-
|
|
3258
|
-
url.slashes = extracted.slashes ||
|
|
3257
|
+
relative2 = !extracted.protocol && !extracted.slashes;
|
|
3258
|
+
url.slashes = extracted.slashes || relative2 && location.slashes;
|
|
3259
3259
|
url.protocol = extracted.protocol || location.protocol || "";
|
|
3260
3260
|
address = extracted.rest;
|
|
3261
3261
|
if (extracted.protocol === "file:" && (extracted.slashesCount !== 2 || windowsDriveLetter.test(address)) || !extracted.slashes && (extracted.protocol || extracted.slashesCount < 2 || !isSpecial(url.protocol))) {
|
|
@@ -3286,12 +3286,12 @@ var require_url_parse = __commonJS({
|
|
|
3286
3286
|
url[key] = index[1];
|
|
3287
3287
|
address = address.slice(0, index.index);
|
|
3288
3288
|
}
|
|
3289
|
-
url[key] = url[key] || (
|
|
3289
|
+
url[key] = url[key] || (relative2 && instruction[3] ? location[key] || "" : "");
|
|
3290
3290
|
if (instruction[4]) url[key] = url[key].toLowerCase();
|
|
3291
3291
|
}
|
|
3292
3292
|
if (parser) url.query = parser(url.query);
|
|
3293
|
-
if (
|
|
3294
|
-
url.pathname =
|
|
3293
|
+
if (relative2 && location.slashes && url.pathname.charAt(0) !== "/" && (url.pathname !== "" || location.pathname !== "")) {
|
|
3294
|
+
url.pathname = resolve3(url.pathname, location.pathname);
|
|
3295
3295
|
}
|
|
3296
3296
|
if (url.pathname.charAt(0) !== "/" && isSpecial(url.protocol)) {
|
|
3297
3297
|
url.pathname = "/" + url.pathname;
|
|
@@ -3420,9 +3420,9 @@ var require_url_parse = __commonJS({
|
|
|
3420
3420
|
}
|
|
3421
3421
|
});
|
|
3422
3422
|
|
|
3423
|
-
//
|
|
3423
|
+
// ../../node_modules/is-stream/index.js
|
|
3424
3424
|
var require_is_stream = __commonJS({
|
|
3425
|
-
"
|
|
3425
|
+
"../../node_modules/is-stream/index.js"(exports, module) {
|
|
3426
3426
|
"use strict";
|
|
3427
3427
|
var isStream2 = (stream) => stream !== null && typeof stream === "object" && typeof stream.pipe === "function";
|
|
3428
3428
|
isStream2.writable = (stream) => isStream2(stream) && stream.writable !== false && typeof stream._write === "function" && typeof stream._writableState === "object";
|
|
@@ -3433,9 +3433,9 @@ var require_is_stream = __commonJS({
|
|
|
3433
3433
|
}
|
|
3434
3434
|
});
|
|
3435
3435
|
|
|
3436
|
-
//
|
|
3436
|
+
// ../../node_modules/lodash.throttle/index.js
|
|
3437
3437
|
var require_lodash = __commonJS({
|
|
3438
|
-
"
|
|
3438
|
+
"../../node_modules/lodash.throttle/index.js"(exports, module) {
|
|
3439
3439
|
"use strict";
|
|
3440
3440
|
var FUNC_ERROR_TEXT = "Expected a function";
|
|
3441
3441
|
var NAN = 0 / 0;
|
|
@@ -3582,9 +3582,9 @@ var require_lodash = __commonJS({
|
|
|
3582
3582
|
}
|
|
3583
3583
|
});
|
|
3584
3584
|
|
|
3585
|
-
//
|
|
3585
|
+
// ../../node_modules/custom-error-instance/bin/factories.js
|
|
3586
3586
|
var require_factories = __commonJS({
|
|
3587
|
-
"
|
|
3587
|
+
"../../node_modules/custom-error-instance/bin/factories.js"(exports) {
|
|
3588
3588
|
"use strict";
|
|
3589
3589
|
exports.expectReceive = function(properties, configuration, factory) {
|
|
3590
3590
|
var message;
|
|
@@ -3655,9 +3655,9 @@ var require_factories = __commonJS({
|
|
|
3655
3655
|
}
|
|
3656
3656
|
});
|
|
3657
3657
|
|
|
3658
|
-
//
|
|
3658
|
+
// ../../node_modules/custom-error-instance/bin/error.js
|
|
3659
3659
|
var require_error2 = __commonJS({
|
|
3660
|
-
"
|
|
3660
|
+
"../../node_modules/custom-error-instance/bin/error.js"(exports, module) {
|
|
3661
3661
|
"use strict";
|
|
3662
3662
|
module.exports = CustomError;
|
|
3663
3663
|
CustomError.factory = require_factories();
|
|
@@ -3766,17 +3766,17 @@ var require_error2 = __commonJS({
|
|
|
3766
3766
|
}
|
|
3767
3767
|
});
|
|
3768
3768
|
|
|
3769
|
-
//
|
|
3769
|
+
// ../../node_modules/custom-error-instance/index.js
|
|
3770
3770
|
var require_custom_error_instance = __commonJS({
|
|
3771
|
-
"
|
|
3771
|
+
"../../node_modules/custom-error-instance/index.js"(exports, module) {
|
|
3772
3772
|
"use strict";
|
|
3773
3773
|
module.exports = require_error2();
|
|
3774
3774
|
}
|
|
3775
3775
|
});
|
|
3776
3776
|
|
|
3777
|
-
//
|
|
3777
|
+
// ../../node_modules/lodash._basetostring/index.js
|
|
3778
3778
|
var require_lodash2 = __commonJS({
|
|
3779
|
-
"
|
|
3779
|
+
"../../node_modules/lodash._basetostring/index.js"(exports, module) {
|
|
3780
3780
|
"use strict";
|
|
3781
3781
|
var INFINITY = 1 / 0;
|
|
3782
3782
|
var symbolTag = "[object Symbol]";
|
|
@@ -3819,9 +3819,9 @@ var require_lodash2 = __commonJS({
|
|
|
3819
3819
|
}
|
|
3820
3820
|
});
|
|
3821
3821
|
|
|
3822
|
-
//
|
|
3822
|
+
// ../../node_modules/lodash._stringtopath/index.js
|
|
3823
3823
|
var require_lodash3 = __commonJS({
|
|
3824
|
-
"
|
|
3824
|
+
"../../node_modules/lodash._stringtopath/index.js"(exports, module) {
|
|
3825
3825
|
"use strict";
|
|
3826
3826
|
var baseToString = require_lodash2();
|
|
3827
3827
|
var FUNC_ERROR_TEXT = "Expected a function";
|
|
@@ -4064,9 +4064,9 @@ var require_lodash3 = __commonJS({
|
|
|
4064
4064
|
}
|
|
4065
4065
|
});
|
|
4066
4066
|
|
|
4067
|
-
//
|
|
4067
|
+
// ../../node_modules/lodash._baseiteratee/index.js
|
|
4068
4068
|
var require_lodash4 = __commonJS({
|
|
4069
|
-
"
|
|
4069
|
+
"../../node_modules/lodash._baseiteratee/index.js"(exports, module) {
|
|
4070
4070
|
"use strict";
|
|
4071
4071
|
var stringToPath = require_lodash3();
|
|
4072
4072
|
var LARGE_ARRAY_SIZE = 200;
|
|
@@ -4835,9 +4835,9 @@ var require_lodash4 = __commonJS({
|
|
|
4835
4835
|
}
|
|
4836
4836
|
});
|
|
4837
4837
|
|
|
4838
|
-
//
|
|
4838
|
+
// ../../node_modules/lodash._createset/index.js
|
|
4839
4839
|
var require_lodash5 = __commonJS({
|
|
4840
|
-
"
|
|
4840
|
+
"../../node_modules/lodash._createset/index.js"(exports, module) {
|
|
4841
4841
|
"use strict";
|
|
4842
4842
|
var INFINITY = 1 / 0;
|
|
4843
4843
|
var funcTag = "[object Function]";
|
|
@@ -4924,9 +4924,9 @@ var require_lodash5 = __commonJS({
|
|
|
4924
4924
|
}
|
|
4925
4925
|
});
|
|
4926
4926
|
|
|
4927
|
-
//
|
|
4927
|
+
// ../../node_modules/lodash._root/index.js
|
|
4928
4928
|
var require_lodash6 = __commonJS({
|
|
4929
|
-
"
|
|
4929
|
+
"../../node_modules/lodash._root/index.js"(exports, module) {
|
|
4930
4930
|
"use strict";
|
|
4931
4931
|
var objectTypes = {
|
|
4932
4932
|
"function": true,
|
|
@@ -4946,9 +4946,9 @@ var require_lodash6 = __commonJS({
|
|
|
4946
4946
|
}
|
|
4947
4947
|
});
|
|
4948
4948
|
|
|
4949
|
-
//
|
|
4949
|
+
// ../../node_modules/lodash._baseuniq/index.js
|
|
4950
4950
|
var require_lodash7 = __commonJS({
|
|
4951
|
-
"
|
|
4951
|
+
"../../node_modules/lodash._baseuniq/index.js"(exports, module) {
|
|
4952
4952
|
"use strict";
|
|
4953
4953
|
var createSet = require_lodash5();
|
|
4954
4954
|
var root = require_lodash6();
|
|
@@ -5249,9 +5249,9 @@ var require_lodash7 = __commonJS({
|
|
|
5249
5249
|
}
|
|
5250
5250
|
});
|
|
5251
5251
|
|
|
5252
|
-
//
|
|
5252
|
+
// ../../node_modules/lodash.uniqby/index.js
|
|
5253
5253
|
var require_lodash8 = __commonJS({
|
|
5254
|
-
"
|
|
5254
|
+
"../../node_modules/lodash.uniqby/index.js"(exports, module) {
|
|
5255
5255
|
"use strict";
|
|
5256
5256
|
var baseIteratee = require_lodash4();
|
|
5257
5257
|
var baseUniq = require_lodash7();
|
|
@@ -5262,9 +5262,9 @@ var require_lodash8 = __commonJS({
|
|
|
5262
5262
|
}
|
|
5263
5263
|
});
|
|
5264
5264
|
|
|
5265
|
-
//
|
|
5265
|
+
// ../../node_modules/combine-errors/index.js
|
|
5266
5266
|
var require_combine_errors = __commonJS({
|
|
5267
|
-
"
|
|
5267
|
+
"../../node_modules/combine-errors/index.js"(exports, module) {
|
|
5268
5268
|
"use strict";
|
|
5269
5269
|
var Custom = require_custom_error_instance();
|
|
5270
5270
|
var uniq = require_lodash8();
|
|
@@ -5298,9 +5298,9 @@ var require_combine_errors = __commonJS({
|
|
|
5298
5298
|
}
|
|
5299
5299
|
});
|
|
5300
5300
|
|
|
5301
|
-
//
|
|
5301
|
+
// ../../node_modules/graceful-fs/polyfills.js
|
|
5302
5302
|
var require_polyfills = __commonJS({
|
|
5303
|
-
"
|
|
5303
|
+
"../../node_modules/graceful-fs/polyfills.js"(exports, module) {
|
|
5304
5304
|
"use strict";
|
|
5305
5305
|
var constants = __require("constants");
|
|
5306
5306
|
var origCwd = process.cwd;
|
|
@@ -5587,9 +5587,9 @@ var require_polyfills = __commonJS({
|
|
|
5587
5587
|
}
|
|
5588
5588
|
});
|
|
5589
5589
|
|
|
5590
|
-
//
|
|
5590
|
+
// ../../node_modules/graceful-fs/legacy-streams.js
|
|
5591
5591
|
var require_legacy_streams = __commonJS({
|
|
5592
|
-
"
|
|
5592
|
+
"../../node_modules/graceful-fs/legacy-streams.js"(exports, module) {
|
|
5593
5593
|
"use strict";
|
|
5594
5594
|
var Stream = __require("stream").Stream;
|
|
5595
5595
|
module.exports = legacy;
|
|
@@ -5684,9 +5684,9 @@ var require_legacy_streams = __commonJS({
|
|
|
5684
5684
|
}
|
|
5685
5685
|
});
|
|
5686
5686
|
|
|
5687
|
-
//
|
|
5687
|
+
// ../../node_modules/graceful-fs/clone.js
|
|
5688
5688
|
var require_clone = __commonJS({
|
|
5689
|
-
"
|
|
5689
|
+
"../../node_modules/graceful-fs/clone.js"(exports, module) {
|
|
5690
5690
|
"use strict";
|
|
5691
5691
|
module.exports = clone;
|
|
5692
5692
|
var getPrototypeOf = Object.getPrototypeOf || function(obj) {
|
|
@@ -5707,9 +5707,9 @@ var require_clone = __commonJS({
|
|
|
5707
5707
|
}
|
|
5708
5708
|
});
|
|
5709
5709
|
|
|
5710
|
-
//
|
|
5710
|
+
// ../../node_modules/graceful-fs/graceful-fs.js
|
|
5711
5711
|
var require_graceful_fs = __commonJS({
|
|
5712
|
-
"
|
|
5712
|
+
"../../node_modules/graceful-fs/graceful-fs.js"(exports, module) {
|
|
5713
5713
|
"use strict";
|
|
5714
5714
|
var fs2 = __require("fs");
|
|
5715
5715
|
var polyfills = require_polyfills();
|
|
@@ -5793,8 +5793,8 @@ var require_graceful_fs = __commonJS({
|
|
|
5793
5793
|
fs3.createReadStream = createReadStream3;
|
|
5794
5794
|
fs3.createWriteStream = createWriteStream;
|
|
5795
5795
|
var fs$readFile = fs3.readFile;
|
|
5796
|
-
fs3.readFile =
|
|
5797
|
-
function
|
|
5796
|
+
fs3.readFile = readFile6;
|
|
5797
|
+
function readFile6(path2, options, cb) {
|
|
5798
5798
|
if (typeof options === "function")
|
|
5799
5799
|
cb = options, options = null;
|
|
5800
5800
|
return go$readFile(path2, options, cb);
|
|
@@ -5865,9 +5865,9 @@ var require_graceful_fs = __commonJS({
|
|
|
5865
5865
|
}
|
|
5866
5866
|
}
|
|
5867
5867
|
var fs$readdir = fs3.readdir;
|
|
5868
|
-
fs3.readdir =
|
|
5868
|
+
fs3.readdir = readdir2;
|
|
5869
5869
|
var noReaddirOptionVersions = /^v[0-5]\./;
|
|
5870
|
-
function
|
|
5870
|
+
function readdir2(path2, options, cb) {
|
|
5871
5871
|
if (typeof options === "function")
|
|
5872
5872
|
cb = options, options = null;
|
|
5873
5873
|
var go$readdir = noReaddirOptionVersions.test(process.version) ? function go$readdir2(path3, options2, cb2, startTime) {
|
|
@@ -6078,9 +6078,9 @@ var require_graceful_fs = __commonJS({
|
|
|
6078
6078
|
}
|
|
6079
6079
|
});
|
|
6080
6080
|
|
|
6081
|
-
//
|
|
6081
|
+
// ../../node_modules/proper-lockfile/node_modules/retry/lib/retry_operation.js
|
|
6082
6082
|
var require_retry_operation = __commonJS({
|
|
6083
|
-
"
|
|
6083
|
+
"../../node_modules/proper-lockfile/node_modules/retry/lib/retry_operation.js"(exports, module) {
|
|
6084
6084
|
"use strict";
|
|
6085
6085
|
function RetryOperation(timeouts, options) {
|
|
6086
6086
|
if (typeof options === "boolean") {
|
|
@@ -6198,11 +6198,11 @@ var require_retry_operation = __commonJS({
|
|
|
6198
6198
|
for (var i = 0; i < this._errors.length; i++) {
|
|
6199
6199
|
var error = this._errors[i];
|
|
6200
6200
|
var message = error.message;
|
|
6201
|
-
var
|
|
6202
|
-
counts[message] =
|
|
6203
|
-
if (
|
|
6201
|
+
var count2 = (counts[message] || 0) + 1;
|
|
6202
|
+
counts[message] = count2;
|
|
6203
|
+
if (count2 >= mainErrorCount) {
|
|
6204
6204
|
mainError = error;
|
|
6205
|
-
mainErrorCount =
|
|
6205
|
+
mainErrorCount = count2;
|
|
6206
6206
|
}
|
|
6207
6207
|
}
|
|
6208
6208
|
return mainError;
|
|
@@ -6210,9 +6210,9 @@ var require_retry_operation = __commonJS({
|
|
|
6210
6210
|
}
|
|
6211
6211
|
});
|
|
6212
6212
|
|
|
6213
|
-
//
|
|
6213
|
+
// ../../node_modules/proper-lockfile/node_modules/retry/lib/retry.js
|
|
6214
6214
|
var require_retry = __commonJS({
|
|
6215
|
-
"
|
|
6215
|
+
"../../node_modules/proper-lockfile/node_modules/retry/lib/retry.js"(exports) {
|
|
6216
6216
|
"use strict";
|
|
6217
6217
|
var RetryOperation = require_retry_operation();
|
|
6218
6218
|
exports.operation = function(options) {
|
|
@@ -6297,17 +6297,17 @@ var require_retry = __commonJS({
|
|
|
6297
6297
|
}
|
|
6298
6298
|
});
|
|
6299
6299
|
|
|
6300
|
-
//
|
|
6300
|
+
// ../../node_modules/proper-lockfile/node_modules/retry/index.js
|
|
6301
6301
|
var require_retry2 = __commonJS({
|
|
6302
|
-
"
|
|
6302
|
+
"../../node_modules/proper-lockfile/node_modules/retry/index.js"(exports, module) {
|
|
6303
6303
|
"use strict";
|
|
6304
6304
|
module.exports = require_retry();
|
|
6305
6305
|
}
|
|
6306
6306
|
});
|
|
6307
6307
|
|
|
6308
|
-
//
|
|
6308
|
+
// ../../node_modules/proper-lockfile/node_modules/signal-exit/signals.js
|
|
6309
6309
|
var require_signals = __commonJS({
|
|
6310
|
-
"
|
|
6310
|
+
"../../node_modules/proper-lockfile/node_modules/signal-exit/signals.js"(exports, module) {
|
|
6311
6311
|
"use strict";
|
|
6312
6312
|
module.exports = [
|
|
6313
6313
|
"SIGABRT",
|
|
@@ -6343,9 +6343,9 @@ var require_signals = __commonJS({
|
|
|
6343
6343
|
}
|
|
6344
6344
|
});
|
|
6345
6345
|
|
|
6346
|
-
//
|
|
6346
|
+
// ../../node_modules/proper-lockfile/node_modules/signal-exit/index.js
|
|
6347
6347
|
var require_signal_exit = __commonJS({
|
|
6348
|
-
"
|
|
6348
|
+
"../../node_modules/proper-lockfile/node_modules/signal-exit/index.js"(exports, module) {
|
|
6349
6349
|
"use strict";
|
|
6350
6350
|
var process2 = global.process;
|
|
6351
6351
|
var processOk = function(process3) {
|
|
@@ -6503,19 +6503,19 @@ var require_signal_exit = __commonJS({
|
|
|
6503
6503
|
}
|
|
6504
6504
|
});
|
|
6505
6505
|
|
|
6506
|
-
//
|
|
6506
|
+
// ../../node_modules/proper-lockfile/lib/mtime-precision.js
|
|
6507
6507
|
var require_mtime_precision = __commonJS({
|
|
6508
|
-
"
|
|
6508
|
+
"../../node_modules/proper-lockfile/lib/mtime-precision.js"(exports, module) {
|
|
6509
6509
|
"use strict";
|
|
6510
6510
|
var cacheSymbol = /* @__PURE__ */ Symbol();
|
|
6511
6511
|
function probe(file, fs2, callback) {
|
|
6512
6512
|
const cachedPrecision = fs2[cacheSymbol];
|
|
6513
6513
|
if (cachedPrecision) {
|
|
6514
|
-
return fs2.stat(file, (err,
|
|
6514
|
+
return fs2.stat(file, (err, stat6) => {
|
|
6515
6515
|
if (err) {
|
|
6516
6516
|
return callback(err);
|
|
6517
6517
|
}
|
|
6518
|
-
callback(null,
|
|
6518
|
+
callback(null, stat6.mtime, cachedPrecision);
|
|
6519
6519
|
});
|
|
6520
6520
|
}
|
|
6521
6521
|
const mtime = new Date(Math.ceil(Date.now() / 1e3) * 1e3 + 5);
|
|
@@ -6523,13 +6523,13 @@ var require_mtime_precision = __commonJS({
|
|
|
6523
6523
|
if (err) {
|
|
6524
6524
|
return callback(err);
|
|
6525
6525
|
}
|
|
6526
|
-
fs2.stat(file, (err2,
|
|
6526
|
+
fs2.stat(file, (err2, stat6) => {
|
|
6527
6527
|
if (err2) {
|
|
6528
6528
|
return callback(err2);
|
|
6529
6529
|
}
|
|
6530
|
-
const precision =
|
|
6530
|
+
const precision = stat6.mtime.getTime() % 1e3 === 0 ? "s" : "ms";
|
|
6531
6531
|
Object.defineProperty(fs2, cacheSymbol, { value: precision });
|
|
6532
|
-
callback(null,
|
|
6532
|
+
callback(null, stat6.mtime, precision);
|
|
6533
6533
|
});
|
|
6534
6534
|
});
|
|
6535
6535
|
}
|
|
@@ -6545,9 +6545,9 @@ var require_mtime_precision = __commonJS({
|
|
|
6545
6545
|
}
|
|
6546
6546
|
});
|
|
6547
6547
|
|
|
6548
|
-
//
|
|
6548
|
+
// ../../node_modules/proper-lockfile/lib/lockfile.js
|
|
6549
6549
|
var require_lockfile = __commonJS({
|
|
6550
|
-
"
|
|
6550
|
+
"../../node_modules/proper-lockfile/lib/lockfile.js"(exports, module) {
|
|
6551
6551
|
"use strict";
|
|
6552
6552
|
var path2 = __require("path");
|
|
6553
6553
|
var fs2 = require_graceful_fs();
|
|
@@ -6583,14 +6583,14 @@ var require_lockfile = __commonJS({
|
|
|
6583
6583
|
if (options.stale <= 0) {
|
|
6584
6584
|
return callback(Object.assign(new Error("Lock file is already being held"), { code: "ELOCKED", file }));
|
|
6585
6585
|
}
|
|
6586
|
-
options.fs.stat(lockfilePath, (err2,
|
|
6586
|
+
options.fs.stat(lockfilePath, (err2, stat6) => {
|
|
6587
6587
|
if (err2) {
|
|
6588
6588
|
if (err2.code === "ENOENT") {
|
|
6589
6589
|
return acquireLock(file, { ...options, stale: 0 }, callback);
|
|
6590
6590
|
}
|
|
6591
6591
|
return callback(err2);
|
|
6592
6592
|
}
|
|
6593
|
-
if (!isLockStale(
|
|
6593
|
+
if (!isLockStale(stat6, options)) {
|
|
6594
6594
|
return callback(Object.assign(new Error("Lock file is already being held"), { code: "ELOCKED", file }));
|
|
6595
6595
|
}
|
|
6596
6596
|
removeLock(file, options, (err3) => {
|
|
@@ -6602,8 +6602,8 @@ var require_lockfile = __commonJS({
|
|
|
6602
6602
|
});
|
|
6603
6603
|
});
|
|
6604
6604
|
}
|
|
6605
|
-
function isLockStale(
|
|
6606
|
-
return
|
|
6605
|
+
function isLockStale(stat6, options) {
|
|
6606
|
+
return stat6.mtime.getTime() < Date.now() - options.stale;
|
|
6607
6607
|
}
|
|
6608
6608
|
function removeLock(file, options, callback) {
|
|
6609
6609
|
options.fs.rmdir(getLockFile(file, options), (err) => {
|
|
@@ -6621,7 +6621,7 @@ var require_lockfile = __commonJS({
|
|
|
6621
6621
|
lock3.updateDelay = lock3.updateDelay || options.update;
|
|
6622
6622
|
lock3.updateTimeout = setTimeout(() => {
|
|
6623
6623
|
lock3.updateTimeout = null;
|
|
6624
|
-
options.fs.stat(lock3.lockfilePath, (err,
|
|
6624
|
+
options.fs.stat(lock3.lockfilePath, (err, stat6) => {
|
|
6625
6625
|
const isOverThreshold = lock3.lastUpdate + options.stale < Date.now();
|
|
6626
6626
|
if (err) {
|
|
6627
6627
|
if (err.code === "ENOENT" || isOverThreshold) {
|
|
@@ -6630,7 +6630,7 @@ var require_lockfile = __commonJS({
|
|
|
6630
6630
|
lock3.updateDelay = 1e3;
|
|
6631
6631
|
return updateLock(file, options);
|
|
6632
6632
|
}
|
|
6633
|
-
const isMtimeOurs = lock3.mtime.getTime() ===
|
|
6633
|
+
const isMtimeOurs = lock3.mtime.getTime() === stat6.mtime.getTime();
|
|
6634
6634
|
if (!isMtimeOurs) {
|
|
6635
6635
|
return setLockAsCompromised(
|
|
6636
6636
|
file,
|
|
@@ -6755,11 +6755,11 @@ var require_lockfile = __commonJS({
|
|
|
6755
6755
|
if (err) {
|
|
6756
6756
|
return callback(err);
|
|
6757
6757
|
}
|
|
6758
|
-
options.fs.stat(getLockFile(file2, options), (err2,
|
|
6758
|
+
options.fs.stat(getLockFile(file2, options), (err2, stat6) => {
|
|
6759
6759
|
if (err2) {
|
|
6760
6760
|
return err2.code === "ENOENT" ? callback(null, false) : callback(err2);
|
|
6761
6761
|
}
|
|
6762
|
-
return callback(null, !isLockStale(
|
|
6762
|
+
return callback(null, !isLockStale(stat6, options));
|
|
6763
6763
|
});
|
|
6764
6764
|
});
|
|
6765
6765
|
}
|
|
@@ -6782,9 +6782,9 @@ var require_lockfile = __commonJS({
|
|
|
6782
6782
|
}
|
|
6783
6783
|
});
|
|
6784
6784
|
|
|
6785
|
-
//
|
|
6785
|
+
// ../../node_modules/proper-lockfile/lib/adapter.js
|
|
6786
6786
|
var require_adapter = __commonJS({
|
|
6787
|
-
"
|
|
6787
|
+
"../../node_modules/proper-lockfile/lib/adapter.js"(exports, module) {
|
|
6788
6788
|
"use strict";
|
|
6789
6789
|
var fs2 = require_graceful_fs();
|
|
6790
6790
|
function createSyncFs(fs3) {
|
|
@@ -6805,12 +6805,12 @@ var require_adapter = __commonJS({
|
|
|
6805
6805
|
return newFs;
|
|
6806
6806
|
}
|
|
6807
6807
|
function toPromise(method) {
|
|
6808
|
-
return (...args) => new Promise((
|
|
6808
|
+
return (...args) => new Promise((resolve3, reject) => {
|
|
6809
6809
|
args.push((err, result) => {
|
|
6810
6810
|
if (err) {
|
|
6811
6811
|
reject(err);
|
|
6812
6812
|
} else {
|
|
6813
|
-
|
|
6813
|
+
resolve3(result);
|
|
6814
6814
|
}
|
|
6815
6815
|
});
|
|
6816
6816
|
method(...args);
|
|
@@ -6847,9 +6847,9 @@ var require_adapter = __commonJS({
|
|
|
6847
6847
|
}
|
|
6848
6848
|
});
|
|
6849
6849
|
|
|
6850
|
-
//
|
|
6850
|
+
// ../../node_modules/proper-lockfile/index.js
|
|
6851
6851
|
var require_proper_lockfile = __commonJS({
|
|
6852
|
-
"
|
|
6852
|
+
"../../node_modules/proper-lockfile/index.js"(exports, module) {
|
|
6853
6853
|
"use strict";
|
|
6854
6854
|
var lockfile2 = require_lockfile();
|
|
6855
6855
|
var { toPromise, toSync, toSyncOptions } = require_adapter();
|
|
@@ -6883,7 +6883,7 @@ var require_proper_lockfile = __commonJS({
|
|
|
6883
6883
|
}
|
|
6884
6884
|
});
|
|
6885
6885
|
|
|
6886
|
-
//
|
|
6886
|
+
// node_modules/commander/esm.mjs
|
|
6887
6887
|
var import_index = __toESM(require_commander(), 1);
|
|
6888
6888
|
var {
|
|
6889
6889
|
program,
|
|
@@ -6909,6 +6909,7 @@ import { homedir } from "os";
|
|
|
6909
6909
|
import { dirname, join } from "path";
|
|
6910
6910
|
var CREDENTIALS_STORE_VERSION = 2;
|
|
6911
6911
|
var MEMBER_PROFILE_PREFIX = "member:";
|
|
6912
|
+
var AGENT_PROFILE_PREFIX = "agent:";
|
|
6912
6913
|
var FALLBACK_PROFILE_KEY = "default";
|
|
6913
6914
|
function configDir() {
|
|
6914
6915
|
return process.env.ZANFIA_CONFIG_DIR || join(homedir(), ".config", "zanfia");
|
|
@@ -6947,6 +6948,9 @@ function profileKeyFor(credentials) {
|
|
|
6947
6948
|
const claims = decodeTokenClaims(credentials.apiKey);
|
|
6948
6949
|
const kind = credentials.kind ?? claims.kind;
|
|
6949
6950
|
const base = credentials.workspaceId ?? claims.workspaceId ?? FALLBACK_PROFILE_KEY;
|
|
6951
|
+
if (kind === "agent") {
|
|
6952
|
+
return `${AGENT_PROFILE_PREFIX}${claims.agentId ?? FALLBACK_PROFILE_KEY}@${base}`;
|
|
6953
|
+
}
|
|
6950
6954
|
return kind === "member" ? `${MEMBER_PROFILE_PREFIX}${base}` : base;
|
|
6951
6955
|
}
|
|
6952
6956
|
function parseCredentialsStore(raw) {
|
|
@@ -7066,8 +7070,9 @@ function decodeTokenClaims(token) {
|
|
|
7066
7070
|
try {
|
|
7067
7071
|
const p = JSON.parse(Buffer.from(parts[1], "base64url").toString("utf8"));
|
|
7068
7072
|
return {
|
|
7069
|
-
kind: p.kind
|
|
7073
|
+
kind: tokenKindOf(p.kind),
|
|
7070
7074
|
workspaceId: typeof p.wsp === "string" ? p.wsp : void 0,
|
|
7075
|
+
agentId: typeof p.agt === "string" ? p.agt : void 0,
|
|
7071
7076
|
subject: typeof p.sub === "string" ? p.sub : void 0,
|
|
7072
7077
|
expiresAt: typeof p.exp === "number" ? new Date(p.exp * 1e3).toISOString() : void 0,
|
|
7073
7078
|
scopes: typeof p.scope === "string" ? p.scope.split(" ").filter(Boolean) : void 0
|
|
@@ -7076,6 +7081,10 @@ function decodeTokenClaims(token) {
|
|
|
7076
7081
|
return { kind: "workspace" };
|
|
7077
7082
|
}
|
|
7078
7083
|
}
|
|
7084
|
+
function tokenKindOf(claim) {
|
|
7085
|
+
if (claim === "member" || claim === "agent") return claim;
|
|
7086
|
+
return "workspace";
|
|
7087
|
+
}
|
|
7079
7088
|
function decodeTokenKind(token) {
|
|
7080
7089
|
return decodeTokenClaims(token).kind;
|
|
7081
7090
|
}
|
|
@@ -8351,6 +8360,9 @@ function createMockClient() {
|
|
|
8351
8360
|
async activatePageAutomations() {
|
|
8352
8361
|
return { delivery: null, followUps: [] };
|
|
8353
8362
|
},
|
|
8363
|
+
async startPageFamilyStep() {
|
|
8364
|
+
throw new ApiError(501, "page family steps are not available in mock mode");
|
|
8365
|
+
},
|
|
8354
8366
|
async listSocialAccounts() {
|
|
8355
8367
|
return { count: 0, accounts: [] };
|
|
8356
8368
|
},
|
|
@@ -8374,6 +8386,42 @@ function createMockClient() {
|
|
|
8374
8386
|
async publishSocialPost(id) {
|
|
8375
8387
|
return { postId: id, status: "published", results: [] };
|
|
8376
8388
|
},
|
|
8389
|
+
async updateSocialPost(id) {
|
|
8390
|
+
throw new ApiError(404, `Social post ${id} not found`);
|
|
8391
|
+
},
|
|
8392
|
+
async unscheduleSocialPost(id) {
|
|
8393
|
+
throw new ApiError(404, `Social post ${id} not found`);
|
|
8394
|
+
},
|
|
8395
|
+
async deleteSocialPost(id) {
|
|
8396
|
+
return { postId: id, deleted: true, previousStatus: "draft" };
|
|
8397
|
+
},
|
|
8398
|
+
async getYouTubeChannel() {
|
|
8399
|
+
throw new ApiError(404, "No YouTube account is connected to this workspace (mock mode)");
|
|
8400
|
+
},
|
|
8401
|
+
async getYouTubeChannelAnalytics() {
|
|
8402
|
+
throw new ApiError(404, "No YouTube account is connected to this workspace (mock mode)");
|
|
8403
|
+
},
|
|
8404
|
+
async listYouTubeVideos() {
|
|
8405
|
+
return { videos: [], nextPageToken: null };
|
|
8406
|
+
},
|
|
8407
|
+
async getYouTubeVideo(videoId) {
|
|
8408
|
+
throw new ApiError(404, `Video ${videoId} is not a video of the connected channel (mock)`);
|
|
8409
|
+
},
|
|
8410
|
+
async getYouTubeVideoTranscript(videoId) {
|
|
8411
|
+
throw new ApiError(404, `Video ${videoId} is not a video of the connected channel (mock)`);
|
|
8412
|
+
},
|
|
8413
|
+
async getInstagramInsights() {
|
|
8414
|
+
throw new ApiError(404, "No Instagram account is connected to this workspace (mock mode)");
|
|
8415
|
+
},
|
|
8416
|
+
async listInstagramMedia() {
|
|
8417
|
+
return { items: [], nextCursor: null };
|
|
8418
|
+
},
|
|
8419
|
+
async listThreadsPosts() {
|
|
8420
|
+
return { items: [] };
|
|
8421
|
+
},
|
|
8422
|
+
async getLatestSocialSnapshot() {
|
|
8423
|
+
return { snapshot: null };
|
|
8424
|
+
},
|
|
8377
8425
|
async uploadMedia(input) {
|
|
8378
8426
|
return {
|
|
8379
8427
|
mediaId: "media_mock",
|
|
@@ -8642,6 +8690,12 @@ function createMockClient() {
|
|
|
8642
8690
|
async reorderDownloadFiles() {
|
|
8643
8691
|
throw new ApiError(501, "downloads content is not available in mock mode");
|
|
8644
8692
|
},
|
|
8693
|
+
async getProductFileSet() {
|
|
8694
|
+
throw new ApiError(501, "product files are not available in mock mode");
|
|
8695
|
+
},
|
|
8696
|
+
async changeProductFileSet() {
|
|
8697
|
+
throw new ApiError(501, "product files are not available in mock mode");
|
|
8698
|
+
},
|
|
8645
8699
|
async listNewsletterIssues() {
|
|
8646
8700
|
throw new ApiError(501, "newsletter content is not available in mock mode");
|
|
8647
8701
|
},
|
|
@@ -8795,6 +8849,9 @@ function createMockClient() {
|
|
|
8795
8849
|
async deleteCommunityEvent() {
|
|
8796
8850
|
throw new ApiError(501, "community events are not available in mock mode");
|
|
8797
8851
|
},
|
|
8852
|
+
async listCommunityEventAttendees() {
|
|
8853
|
+
throw new ApiError(501, "community events are not available in mock mode");
|
|
8854
|
+
},
|
|
8798
8855
|
async getCommunityModerationQueue() {
|
|
8799
8856
|
return { pending: [], resolved: [] };
|
|
8800
8857
|
},
|
|
@@ -8865,7 +8922,7 @@ function createMockClient() {
|
|
|
8865
8922
|
return { rooms: [] };
|
|
8866
8923
|
},
|
|
8867
8924
|
async getLiveRoomAttendance() {
|
|
8868
|
-
throw new ApiError(501, "
|
|
8925
|
+
throw new ApiError(501, "webinars are not available in mock mode");
|
|
8869
8926
|
},
|
|
8870
8927
|
async call(method, pathTemplate) {
|
|
8871
8928
|
throw new ApiError(501, `${method} ${pathTemplate} is not available in mock mode`);
|
|
@@ -8903,6 +8960,9 @@ function createMockClient() {
|
|
|
8903
8960
|
async getMetaAdDirectory() {
|
|
8904
8961
|
throw new ApiError(501, "Meta Ads are not available in mock mode");
|
|
8905
8962
|
},
|
|
8963
|
+
async listMetaAdConnections() {
|
|
8964
|
+
throw new ApiError(501, "Meta Ads are not available in mock mode");
|
|
8965
|
+
},
|
|
8906
8966
|
async listMetaCampaigns() {
|
|
8907
8967
|
throw new ApiError(501, "Meta Ads are not available in mock mode");
|
|
8908
8968
|
},
|
|
@@ -9104,6 +9164,15 @@ function createHttpClient(config) {
|
|
|
9104
9164
|
)
|
|
9105
9165
|
);
|
|
9106
9166
|
}
|
|
9167
|
+
if (tokenKind === "agent") {
|
|
9168
|
+
return Promise.reject(
|
|
9169
|
+
new ApiError(
|
|
9170
|
+
403,
|
|
9171
|
+
"This command uses the Workspace API, which a community agent key can\u2019t access.",
|
|
9172
|
+
"An agent key reads the agent\u2019s communities and writes as the agent: use `zanfia communities \u2026`, or `zanfia login` for the workspace."
|
|
9173
|
+
)
|
|
9174
|
+
);
|
|
9175
|
+
}
|
|
9107
9176
|
return requestFrom(config.apiUrl, method, path2, body);
|
|
9108
9177
|
};
|
|
9109
9178
|
const communityRequest = (method, path2, body) => {
|
|
@@ -9623,7 +9692,7 @@ function createHttpClient(config) {
|
|
|
9623
9692
|
deleteMediaFolder: (name) => request("DELETE", `/media-folders/${encodeURIComponent(name)}`),
|
|
9624
9693
|
initiateVideoUpload: (input) => request("POST", "/media/videos/initiate-upload", input),
|
|
9625
9694
|
updatePage: (id, input) => request("PATCH", `/pages/${encodeURIComponent(id)}`, input),
|
|
9626
|
-
publishPage: (id, publish) => request("POST", `/pages/${encodeURIComponent(id)}/publish`, { publish }),
|
|
9695
|
+
publishPage: (id, publish, options) => request("POST", `/pages/${encodeURIComponent(id)}/publish`, { publish, ...options }),
|
|
9627
9696
|
deletePage: (id) => request("DELETE", `/pages/${encodeURIComponent(id)}`),
|
|
9628
9697
|
setMainPage: (pageId) => request("PUT", "/pages/main", { pageId }),
|
|
9629
9698
|
generatePage: (input) => request("POST", "/pages/generate", input),
|
|
@@ -9635,6 +9704,7 @@ function createHttpClient(config) {
|
|
|
9635
9704
|
`/pages/${encodeURIComponent(id)}/revisions/${encodeURIComponent(revisionId)}/restore`
|
|
9636
9705
|
),
|
|
9637
9706
|
activatePageAutomations: (id) => request("POST", `/pages/${encodeURIComponent(id)}/activate-automations`),
|
|
9707
|
+
startPageFamilyStep: (id, input) => request("POST", `/pages/${encodeURIComponent(id)}/family-step`, input),
|
|
9638
9708
|
listSocialAccounts: () => request("GET", "/social/accounts"),
|
|
9639
9709
|
listSocialPosts: (options) => {
|
|
9640
9710
|
const params = new URLSearchParams();
|
|
@@ -9647,6 +9717,30 @@ function createHttpClient(config) {
|
|
|
9647
9717
|
createSocialPost: (input) => request("POST", "/social/posts", input),
|
|
9648
9718
|
scheduleSocialPost: (id, scheduledAt) => request("POST", `/social/posts/${encodeURIComponent(id)}/schedule`, { scheduledAt }),
|
|
9649
9719
|
publishSocialPost: (id) => request("POST", `/social/posts/${encodeURIComponent(id)}/publish`),
|
|
9720
|
+
updateSocialPost: (id, input) => request("PATCH", `/social/posts/${encodeURIComponent(id)}`, input),
|
|
9721
|
+
unscheduleSocialPost: (id) => request("POST", `/social/posts/${encodeURIComponent(id)}/unschedule`),
|
|
9722
|
+
deleteSocialPost: (id) => request("DELETE", `/social/posts/${encodeURIComponent(id)}`),
|
|
9723
|
+
getYouTubeChannel: () => request("GET", "/social/youtube/channel"),
|
|
9724
|
+
getYouTubeChannelAnalytics: () => request("GET", "/social/youtube/analytics"),
|
|
9725
|
+
listYouTubeVideos: (options) => {
|
|
9726
|
+
const params = new URLSearchParams();
|
|
9727
|
+
if (options?.maxResults) params.set("maxResults", String(options.maxResults));
|
|
9728
|
+
if (options?.pageToken) params.set("pageToken", options.pageToken);
|
|
9729
|
+
const query = params.toString();
|
|
9730
|
+
return request("GET", query ? `/social/youtube/videos?${query}` : "/social/youtube/videos");
|
|
9731
|
+
},
|
|
9732
|
+
getYouTubeVideo: (videoId) => request("GET", `/social/youtube/videos/${encodeURIComponent(videoId)}`),
|
|
9733
|
+
getYouTubeVideoTranscript: (videoId) => request("GET", `/social/youtube/videos/${encodeURIComponent(videoId)}/transcript`),
|
|
9734
|
+
getInstagramInsights: () => request("GET", "/social/instagram/insights"),
|
|
9735
|
+
listInstagramMedia: (options) => {
|
|
9736
|
+
const params = new URLSearchParams();
|
|
9737
|
+
if (options?.limit) params.set("limit", String(options.limit));
|
|
9738
|
+
if (options?.after) params.set("after", options.after);
|
|
9739
|
+
const query = params.toString();
|
|
9740
|
+
return request("GET", query ? `/social/instagram/media?${query}` : "/social/instagram/media");
|
|
9741
|
+
},
|
|
9742
|
+
listThreadsPosts: () => request("GET", "/social/threads/posts"),
|
|
9743
|
+
getLatestSocialSnapshot: () => request("GET", "/social/snapshots/latest"),
|
|
9650
9744
|
// Workspace team lives on the Workspace API (api-v1).
|
|
9651
9745
|
listTeamMembers: () => request("GET", "/team/members"),
|
|
9652
9746
|
listTeamInvitations: () => request("GET", "/team/invitations"),
|
|
@@ -9795,6 +9889,8 @@ function createHttpClient(config) {
|
|
|
9795
9889
|
getDownloads: (productId) => request("GET", `/products/${encodeURIComponent(productId)}/downloads`),
|
|
9796
9890
|
updateDownloadsContent: (productId, editorContent) => request("PATCH", `/products/${encodeURIComponent(productId)}/downloads`, { editorContent }),
|
|
9797
9891
|
addDownloadFiles: (productId, files) => request("POST", `/products/${encodeURIComponent(productId)}/downloads/files`, { files }),
|
|
9892
|
+
getProductFileSet: (productId) => request("GET", `/products/${encodeURIComponent(productId)}/file-set`),
|
|
9893
|
+
changeProductFileSet: (productId, input) => request("POST", `/products/${encodeURIComponent(productId)}/file-set`, input),
|
|
9798
9894
|
removeDownloadFile: (productId, uid) => request(
|
|
9799
9895
|
"DELETE",
|
|
9800
9896
|
`/products/${encodeURIComponent(productId)}/downloads/files/${encodeURIComponent(uid)}`
|
|
@@ -9989,6 +10085,10 @@ function createHttpClient(config) {
|
|
|
9989
10085
|
"DELETE",
|
|
9990
10086
|
`/v1/communities/${encodeURIComponent(communityId)}/events/${encodeURIComponent(eventId)}`
|
|
9991
10087
|
),
|
|
10088
|
+
listCommunityEventAttendees: (communityId, eventId) => communityRequest(
|
|
10089
|
+
"GET",
|
|
10090
|
+
`/v1/communities/${encodeURIComponent(communityId)}/events/${encodeURIComponent(eventId)}/attendees`
|
|
10091
|
+
),
|
|
9992
10092
|
getCommunityModerationQueue: (communityId) => communityRequest(
|
|
9993
10093
|
"GET",
|
|
9994
10094
|
`/v1/communities/${encodeURIComponent(communityId)}/moderation/reports`
|
|
@@ -10069,6 +10169,7 @@ function createHttpClient(config) {
|
|
|
10069
10169
|
// Meta Ads live on the Workspace API (api-v1) under /meta-ads.
|
|
10070
10170
|
getMetaAdsPerformance: (query) => request("GET", `/meta-ads/performance${toQuery(query)}`),
|
|
10071
10171
|
getMetaAdDirectory: () => request("GET", "/meta-ads/directory"),
|
|
10172
|
+
listMetaAdConnections: () => request("GET", "/meta-ads/connections"),
|
|
10072
10173
|
listMetaCampaigns: (scope, query) => request("GET", `${metaAdAccountPath(scope)}/campaigns${toQuery(query)}`),
|
|
10073
10174
|
updateMetaCampaign: (scope, campaignId, input) => request(
|
|
10074
10175
|
"PATCH",
|
|
@@ -10227,6 +10328,22 @@ function parseJsonObjectFileOrInline(flag) {
|
|
|
10227
10328
|
return parsed;
|
|
10228
10329
|
};
|
|
10229
10330
|
}
|
|
10331
|
+
function parseJsonArrayFileOrInline(flag) {
|
|
10332
|
+
return (value) => {
|
|
10333
|
+
const trimmed = value.trim();
|
|
10334
|
+
const raw = trimmed.startsWith("[") ? trimmed : readFileSync2(value, "utf8");
|
|
10335
|
+
let parsed;
|
|
10336
|
+
try {
|
|
10337
|
+
parsed = JSON.parse(raw);
|
|
10338
|
+
} catch (error) {
|
|
10339
|
+
throw new Error(`Invalid JSON for ${flag}: ${error.message}`);
|
|
10340
|
+
}
|
|
10341
|
+
if (!Array.isArray(parsed)) {
|
|
10342
|
+
throw new Error(`${flag} must be a JSON array (inline or a .json file path).`);
|
|
10343
|
+
}
|
|
10344
|
+
return parsed;
|
|
10345
|
+
};
|
|
10346
|
+
}
|
|
10230
10347
|
function requireConfirm(confirm, what) {
|
|
10231
10348
|
if (!confirm) {
|
|
10232
10349
|
throw new Error(`Refusing to ${what} without --confirm. Re-run with --confirm to proceed.`);
|
|
@@ -10690,13 +10807,14 @@ function registerCertificatesCommands(program2) {
|
|
|
10690
10807
|
|
|
10691
10808
|
// src/commands/_generated/blog.ts
|
|
10692
10809
|
var listBlogPostsColumns = ["id", "slug", "title", "status", "publishedAt", "updatedAt"];
|
|
10810
|
+
var listBlogCategoriesColumns = ["id", "name", "slug", "postCount"];
|
|
10693
10811
|
function registerBlogCommands(program2) {
|
|
10694
10812
|
const blog = program2.command("blog");
|
|
10695
10813
|
blog.description(
|
|
10696
10814
|
"The workspace blog (Your site \u2192 Blog): Markdown posts served at /blog on the store host; drafts are visible to the team only"
|
|
10697
10815
|
);
|
|
10698
10816
|
blog.command("list").summary("List blog posts").description(
|
|
10699
|
-
"Every post of the workspace blog (drafts and published), newest change first: id, slug (`/blog/{slug}` on the store host), title, excerpt, cover, tags, status and dates. No body
|
|
10817
|
+
"Every post of the workspace blog (drafts and published), newest change first: id, slug (`/blog/{slug}` on the store host), title, excerpt, cover, tags, status and dates. No body: use get-blog-post for the Markdown. The id source for every other blog tool."
|
|
10700
10818
|
).action(async (_options, command) => {
|
|
10701
10819
|
const flags = getGlobalFlags(command);
|
|
10702
10820
|
const client = createApiClient({ apiKey: flags.apiKey, verbose: flags.verbose });
|
|
@@ -10706,14 +10824,18 @@ function registerBlogCommands(program2) {
|
|
|
10706
10824
|
emit(flags, result ?? {}, () => printTable(rows, listBlogPostsColumns));
|
|
10707
10825
|
});
|
|
10708
10826
|
blog.command("create").summary("Create a blog post").description(
|
|
10709
|
-
"Creates a blog post as a DRAFT (or published with `publish: true` when it has a body). The body is Markdown. A slug the caller sets must be free (409) and not `feed`; a slug derived from the title takes the next free numeric suffix.
|
|
10827
|
+
"Creates a blog post as a DRAFT (or published with `publish: true` when it has a body). The body is Markdown. A slug the caller sets must be free (409) and not `feed`; a slug derived from the title takes the next free numeric suffix. `categoryIds` puts it in up to 3 blog categories (list-blog-categories). To go live later, create the draft and call schedule-blog-post. Returns the full post."
|
|
10710
10828
|
).requiredOption("--title <value>", "Post title (max 200 characters).").option(
|
|
10711
10829
|
"--slug <value>",
|
|
10712
10830
|
"Derived from the title when omitted; a taken derived slug gets a numeric suffix."
|
|
10713
10831
|
).option("--content <value>", "The body as Markdown (headings, lists, links, images by URL).").option(
|
|
10714
10832
|
"--excerpt <value>",
|
|
10715
10833
|
"Short plain-text summary (list, RSS, meta description); derived from the body on publish when empty."
|
|
10716
|
-
).option("--cover <value>", "Cover image URL (Media Library).").option("--tags <a,b,...>", "Lowercase labels, at most 10.", parseCommaList("--tags")).option(
|
|
10834
|
+
).option("--cover <value>", "Cover image URL (Media Library).").option("--tags <a,b,...>", "Lowercase labels, at most 10.", parseCommaList("--tags")).option(
|
|
10835
|
+
"--category-ids <a,b,...>",
|
|
10836
|
+
"Ids of the blog categories (`bcat_\u2026` from list-blog-categories) the post sits in, at most 3; replaces the whole list, [] = none. Tags stay a separate label.",
|
|
10837
|
+
parseCommaList("--category-ids")
|
|
10838
|
+
).option("--seo <json|file>", "seo", parseJsonObjectFileOrInline("--seo")).option("--publish", "Publish right away (needs a non-empty body); default false = draft.").option("--no-publish", "Send `publish: false` explicitly.").option(
|
|
10717
10839
|
"--published-at <value>",
|
|
10718
10840
|
"The post's date, ISO 8601 date (2025-10-16) or date-time, not in the future - e.g. the original date of an imported post. Omitted = the moment of the first publication."
|
|
10719
10841
|
).action(async (options, command) => {
|
|
@@ -10727,6 +10849,7 @@ function registerBlogCommands(program2) {
|
|
|
10727
10849
|
excerpt: options.excerpt,
|
|
10728
10850
|
coverImageUrl: options.cover,
|
|
10729
10851
|
tags: options.tags,
|
|
10852
|
+
categoryIds: options.categoryIds,
|
|
10730
10853
|
seo: options.seo,
|
|
10731
10854
|
publish: options.publish,
|
|
10732
10855
|
publishedAt: options.publishedAt
|
|
@@ -10746,7 +10869,7 @@ function registerBlogCommands(program2) {
|
|
|
10746
10869
|
emit(flags, result ?? {}, () => printJson(result ?? {}));
|
|
10747
10870
|
});
|
|
10748
10871
|
blog.command("update").summary("Update a blog post").description(
|
|
10749
|
-
"Partial update of a blog post: title, slug (must be free, 409), Markdown body, excerpt, cover (null removes it), tags, SEO (whole section replaced). A published post keeps serving the new content at once. Returns the full post."
|
|
10872
|
+
"Partial update of a blog post: title, slug (must be free, 409), Markdown body, excerpt, cover (null removes it), tags, categories (`categoryIds`, the whole list, at most 3), SEO (whole section replaced). A published post keeps serving the new content at once; a scheduled one stays scheduled. Returns the full post."
|
|
10750
10873
|
).argument("<id>", "Blog post id (`post_\u2026`) from list-blog-posts.").option("--title <value>", "title").option(
|
|
10751
10874
|
"--slug <value>",
|
|
10752
10875
|
"Public path segment (`/blog/{slug}`): lowercase letters, digits and hyphens."
|
|
@@ -10754,6 +10877,10 @@ function registerBlogCommands(program2) {
|
|
|
10754
10877
|
"--excerpt <value>",
|
|
10755
10878
|
"Short plain-text summary (list, RSS, meta description); derived from the body on publish when empty."
|
|
10756
10879
|
).option("--cover <value>", "Cover image URL; null removes the cover.").option("--tags <a,b,...>", "Lowercase labels, at most 10.", parseCommaList("--tags")).option(
|
|
10880
|
+
"--category-ids <a,b,...>",
|
|
10881
|
+
"Ids of the blog categories (`bcat_\u2026` from list-blog-categories) the post sits in, at most 3; replaces the whole list, [] = none. Tags stay a separate label.",
|
|
10882
|
+
parseCommaList("--category-ids")
|
|
10883
|
+
).option(
|
|
10757
10884
|
"--seo <json|file>",
|
|
10758
10885
|
"Replaces the whole SEO section.",
|
|
10759
10886
|
parseJsonObjectFileOrInline("--seo")
|
|
@@ -10773,6 +10900,7 @@ function registerBlogCommands(program2) {
|
|
|
10773
10900
|
excerpt: options.excerpt,
|
|
10774
10901
|
coverImageUrl: options.cover,
|
|
10775
10902
|
tags: options.tags,
|
|
10903
|
+
categoryIds: options.categoryIds,
|
|
10776
10904
|
seo: options.seo,
|
|
10777
10905
|
publishedAt: options.publishedAt
|
|
10778
10906
|
};
|
|
@@ -10780,8 +10908,8 @@ function registerBlogCommands(program2) {
|
|
|
10780
10908
|
emit(flags, result ?? {}, () => printJson(result ?? {}));
|
|
10781
10909
|
});
|
|
10782
10910
|
blog.command("publish").summary("Publish or unpublish a blog post").description(
|
|
10783
|
-
"Publishes a post at `/blog/{slug}` (needs a non-empty body, 400 otherwise; the excerpt is derived from the body when empty) or takes it back to draft. The first publication date is the post's date and survives unpublish / republish. Reversible. Returns the full post."
|
|
10784
|
-
).argument("<id>", "Blog post id (`post_\u2026`) from list-blog-posts.").option("--publish", "true = publish, false = back to draft.").action(async (id, options, command) => {
|
|
10911
|
+
"Publishes a post at `/blog/{slug}` now (needs a non-empty body, 400 otherwise; the excerpt is derived from the body when empty) or takes it back to draft. A scheduled post is published at once and its schedule dropped; back to draft drops it too. The first publication date is the post's date and survives unpublish / republish. Reversible. Without `publish` (an empty request body) the post is published. Returns the full post."
|
|
10912
|
+
).argument("<id>", "Blog post id (`post_\u2026`) from list-blog-posts.").option("--publish", "true = publish (the default when absent), false = back to draft.").option("--no-publish", "Send `publish: false` explicitly.").action(async (id, options, command) => {
|
|
10785
10913
|
const flags = getGlobalFlags(command);
|
|
10786
10914
|
const client = createApiClient({ apiKey: flags.apiKey, verbose: flags.verbose });
|
|
10787
10915
|
const params = {
|
|
@@ -10800,7 +10928,7 @@ function registerBlogCommands(program2) {
|
|
|
10800
10928
|
emit(flags, result ?? {}, () => printJson(result ?? {}));
|
|
10801
10929
|
});
|
|
10802
10930
|
blog.command("delete").summary("Delete a blog post").description(
|
|
10803
|
-
"Deletes a blog post for good; a published one disappears from `/blog` at once. No undo
|
|
10931
|
+
"Deletes a blog post for good; a published one disappears from `/blog` at once. No undo; prefer unpublishing."
|
|
10804
10932
|
).argument("<id>", "Blog post id (`post_\u2026`) from list-blog-posts.").option("--confirm", "required \u2014 acknowledge that you want to deletes the post for good").action(async (id, options, command) => {
|
|
10805
10933
|
const flags = getGlobalFlags(command);
|
|
10806
10934
|
requireConfirm(options.confirm, "deletes the post for good");
|
|
@@ -10817,6 +10945,118 @@ function registerBlogCommands(program2) {
|
|
|
10817
10945
|
);
|
|
10818
10946
|
emit(flags, result ?? {}, () => printJson(result ?? {}));
|
|
10819
10947
|
});
|
|
10948
|
+
blog.command("schedule").summary("Schedule a blog post").description(
|
|
10949
|
+
"Schedules a DRAFT (or reschedules a scheduled post) to be published at `scheduledAt`: status `scheduled`, not public anywhere (list, feed, sitemap, its URL) until then, then published exactly like publish-blog-post. Needs a non-empty body (400); a published post answers 409 (unpublish it first). `scheduledAt: null` unschedules (back to draft); publishing it now is publish-blog-post. Returns the full post with `scheduledAt`."
|
|
10950
|
+
).argument("<id>", "Blog post id (`post_\u2026`) from list-blog-posts.").requiredOption(
|
|
10951
|
+
"--at <value>",
|
|
10952
|
+
`When the post goes live: an ISO 8601 date-time WITH the offset of the creator's time zone, Europe/Warsaw = +01:00 in winter (CET), +02:00 in summer (CEST), e.g. "poniedzia\u0142ek 8:00" in late October = 2026-10-26T08:00:00+01:00. At least a minute and at most a year ahead. null = unschedule (back to draft).`
|
|
10953
|
+
).action(async (id, options, command) => {
|
|
10954
|
+
const flags = getGlobalFlags(command);
|
|
10955
|
+
const client = createApiClient({ apiKey: flags.apiKey, verbose: flags.verbose });
|
|
10956
|
+
const params = {
|
|
10957
|
+
postId: id
|
|
10958
|
+
};
|
|
10959
|
+
const payload = {
|
|
10960
|
+
scheduledAt: options.at
|
|
10961
|
+
};
|
|
10962
|
+
const result = await client.call(
|
|
10963
|
+
"POST",
|
|
10964
|
+
"/blog-posts/:postId/schedule",
|
|
10965
|
+
params,
|
|
10966
|
+
void 0,
|
|
10967
|
+
payload
|
|
10968
|
+
);
|
|
10969
|
+
emit(flags, result ?? {}, () => printJson(result ?? {}));
|
|
10970
|
+
});
|
|
10971
|
+
const blogCategories = blog.command("categories");
|
|
10972
|
+
blogCategories.description(
|
|
10973
|
+
"Categories of the workspace blog: a post sits in up to 3, each has its own page"
|
|
10974
|
+
);
|
|
10975
|
+
blogCategories.command("list").summary("List blog categories").description(
|
|
10976
|
+
"The categories of the workspace blog, by name: id, name, slug (public page `/blog/kategoria/{slug}`, `/blog/category/{slug}` on English sites), description, SEO and how many posts sit in each. The id source for `categoryIds` on posts."
|
|
10977
|
+
).action(async (_options, command) => {
|
|
10978
|
+
const flags = getGlobalFlags(command);
|
|
10979
|
+
const client = createApiClient({ apiKey: flags.apiKey, verbose: flags.verbose });
|
|
10980
|
+
const params = {};
|
|
10981
|
+
const result = await client.call("GET", "/blog-categories", params, void 0, void 0);
|
|
10982
|
+
const rows = tableRowsOf(result, "categories");
|
|
10983
|
+
emit(flags, result ?? {}, () => printTable(rows, listBlogCategoriesColumns));
|
|
10984
|
+
});
|
|
10985
|
+
blogCategories.command("create").summary("Create a blog category").description(
|
|
10986
|
+
"Adds a category to the blog (at most 50). A slug the caller sets must be free (409); one derived from the name takes the next free numeric suffix. The category page goes public once a published post sits in it. Returns the category."
|
|
10987
|
+
).argument("<name>", 'Category name (max 60 characters), e.g. "Poradniki".').option(
|
|
10988
|
+
"--slug <value>",
|
|
10989
|
+
"Derived from the name when omitted; a taken derived slug gets a numeric suffix."
|
|
10990
|
+
).option(
|
|
10991
|
+
"--description <value>",
|
|
10992
|
+
"Plain text shown under the category heading; the default meta description."
|
|
10993
|
+
).option("--seo <json|file>", "seo", parseJsonObjectFileOrInline("--seo")).action(async (name, options, command) => {
|
|
10994
|
+
const flags = getGlobalFlags(command);
|
|
10995
|
+
const client = createApiClient({ apiKey: flags.apiKey, verbose: flags.verbose });
|
|
10996
|
+
const params = {};
|
|
10997
|
+
const payload = {
|
|
10998
|
+
name,
|
|
10999
|
+
slug: options.slug,
|
|
11000
|
+
description: options.description,
|
|
11001
|
+
seo: options.seo
|
|
11002
|
+
};
|
|
11003
|
+
const result = await client.call("POST", "/blog-categories", params, void 0, payload);
|
|
11004
|
+
emit(flags, result ?? {}, () => printJson(result ?? {}));
|
|
11005
|
+
});
|
|
11006
|
+
blogCategories.command("update").summary("Update a blog category").description(
|
|
11007
|
+
"Partial update of a category: name, slug (must be free, 409; a new slug moves the public category page and the old address stops working), description, SEO (whole section replaced). Its posts stay in it. Returns the category."
|
|
11008
|
+
).argument("<id>", "Blog category id (`bcat_\u2026`) from list-blog-categories.").option("--name <value>", "name").option(
|
|
11009
|
+
"--slug <value>",
|
|
11010
|
+
"Public path segment of the category page (`/blog/kategoria/{slug}`, `/blog/category/{slug}` on English sites): lowercase letters, digits and hyphens."
|
|
11011
|
+
).option(
|
|
11012
|
+
"--description <value>",
|
|
11013
|
+
"Plain text shown under the category heading; the default meta description."
|
|
11014
|
+
).option(
|
|
11015
|
+
"--seo <json|file>",
|
|
11016
|
+
"Replaces the whole SEO section.",
|
|
11017
|
+
parseJsonObjectFileOrInline("--seo")
|
|
11018
|
+
).action(async (id, options, command) => {
|
|
11019
|
+
const flags = getGlobalFlags(command);
|
|
11020
|
+
const client = createApiClient({ apiKey: flags.apiKey, verbose: flags.verbose });
|
|
11021
|
+
const params = {
|
|
11022
|
+
categoryId: id
|
|
11023
|
+
};
|
|
11024
|
+
const payload = {
|
|
11025
|
+
name: options.name,
|
|
11026
|
+
slug: options.slug,
|
|
11027
|
+
description: options.description,
|
|
11028
|
+
seo: options.seo
|
|
11029
|
+
};
|
|
11030
|
+
const result = await client.call(
|
|
11031
|
+
"PATCH",
|
|
11032
|
+
"/blog-categories/:categoryId",
|
|
11033
|
+
params,
|
|
11034
|
+
void 0,
|
|
11035
|
+
payload
|
|
11036
|
+
);
|
|
11037
|
+
emit(flags, result ?? {}, () => printJson(result ?? {}));
|
|
11038
|
+
});
|
|
11039
|
+
blogCategories.command("delete").summary("Delete a blog category").description(
|
|
11040
|
+
"Deletes a category for good and takes it off every post in it; the posts stay as they are and the category page stops working. No undo."
|
|
11041
|
+
).argument("<id>", "Blog category id (`bcat_\u2026`) from list-blog-categories.").option(
|
|
11042
|
+
"--confirm",
|
|
11043
|
+
"required \u2014 acknowledge that you want to deletes the category for good (its posts stay)"
|
|
11044
|
+
).action(async (id, options, command) => {
|
|
11045
|
+
const flags = getGlobalFlags(command);
|
|
11046
|
+
requireConfirm(options.confirm, "deletes the category for good (its posts stay)");
|
|
11047
|
+
const client = createApiClient({ apiKey: flags.apiKey, verbose: flags.verbose });
|
|
11048
|
+
const params = {
|
|
11049
|
+
categoryId: id
|
|
11050
|
+
};
|
|
11051
|
+
const result = await client.call(
|
|
11052
|
+
"DELETE",
|
|
11053
|
+
"/blog-categories/:categoryId",
|
|
11054
|
+
params,
|
|
11055
|
+
void 0,
|
|
11056
|
+
void 0
|
|
11057
|
+
);
|
|
11058
|
+
emit(flags, result ?? {}, () => printJson(result ?? {}));
|
|
11059
|
+
});
|
|
10820
11060
|
}
|
|
10821
11061
|
|
|
10822
11062
|
// src/commands/_generated/site-redirects.ts
|
|
@@ -10904,10 +11144,10 @@ var listPopupsColumns = ["id", "name", "status", "priority", "updatedAt"];
|
|
|
10904
11144
|
function registerPopupsCommands(program2) {
|
|
10905
11145
|
const popups = program2.command("popups");
|
|
10906
11146
|
popups.description(
|
|
10907
|
-
"Pop-ups on the workspace site (Your site \u2192 Pop-ups): a signup form shown on entry or
|
|
11147
|
+
"Pop-ups on the workspace site (Your site \u2192 Pop-ups): a signup form, an offer or a link shown on entry, after scrolling or on exit intent, after the cookie decision, never again after a conversion"
|
|
10908
11148
|
);
|
|
10909
11149
|
popups.command("list").summary("List pop-ups").description(
|
|
10910
|
-
"Every pop-up of the workspace site (drafts, active, paused) in priority order: copy, form
|
|
11150
|
+
"Every pop-up of the workspace site (drafts, active, paused) in priority order: copy, content (form, offer or link), layout, triggers per device, targeting, frequency and schedule. The id source for every other pop-up tool."
|
|
10911
11151
|
).action(async (_options, command) => {
|
|
10912
11152
|
const flags = getGlobalFlags(command);
|
|
10913
11153
|
const client = createApiClient({ apiKey: flags.apiKey, verbose: flags.verbose });
|
|
@@ -10917,7 +11157,7 @@ function registerPopupsCommands(program2) {
|
|
|
10917
11157
|
emit(flags, result ?? {}, () => printTable(rows, listPopupsColumns));
|
|
10918
11158
|
});
|
|
10919
11159
|
popups.command("create").summary("Create a pop-up").description(
|
|
10920
|
-
"Creates a pop-up as a DRAFT
|
|
11160
|
+
"Creates a pop-up as a DRAFT. Content: a signup form (default; without `content.formId` a new standalone signup form named after the pop-up is created, visible in Forms), an offer (a checkout of a product; the button opens it) or a link button. Defaults: a centred card (`design.layout: slide-in` = a corner card), desktop after 10 s on the page (or exit intent), phones after 40 % scroll as a bottom sheet, again after 7 days, all hosted pages except thank-you / download / member pages (or the blog / the store), everyone (or new / returning visitors), hidden for subscribers. It is turned on with set-popup-status (in Zanfia\u2019s own chat agent, behind an approval card the creator confirms) once it has a title and a button text. Requires the pop-ups feature on the plan (403 otherwise)."
|
|
10921
11161
|
).requiredOption(
|
|
10922
11162
|
"--name <value>",
|
|
10923
11163
|
"Internal name (panel only); also the name of a provisioned form."
|
|
@@ -10936,7 +11176,7 @@ function registerPopupsCommands(program2) {
|
|
|
10936
11176
|
parseJsonObjectFileOrInline("--copy")
|
|
10937
11177
|
).option(
|
|
10938
11178
|
"--content <json|file>",
|
|
10939
|
-
"What the pop-up holds: a signup form.",
|
|
11179
|
+
"What the pop-up holds: a signup form, an offer (koszyk) or a link button.",
|
|
10940
11180
|
parseJsonObjectFileOrInline("--content")
|
|
10941
11181
|
).option("--design <json|file>", "design", parseJsonObjectFileOrInline("--design")).option(
|
|
10942
11182
|
"--trigger <json|file>",
|
|
@@ -10974,7 +11214,7 @@ function registerPopupsCommands(program2) {
|
|
|
10974
11214
|
emit(flags, result ?? {}, () => printJson(result ?? {}));
|
|
10975
11215
|
});
|
|
10976
11216
|
popups.command("get").summary("Pop-up detail").description(
|
|
10977
|
-
"One pop-up in full: status, copy, form
|
|
11217
|
+
"One pop-up in full: status, copy, content (form, offer or link), design, triggers, targeting, frequency, schedule."
|
|
10978
11218
|
).argument("<id>", "Pop-up id (`popup_\u2026`) from list-popups.").action(async (id, _options, command) => {
|
|
10979
11219
|
const flags = getGlobalFlags(command);
|
|
10980
11220
|
const client = createApiClient({ apiKey: flags.apiKey, verbose: flags.verbose });
|
|
@@ -10985,7 +11225,7 @@ function registerPopupsCommands(program2) {
|
|
|
10985
11225
|
emit(flags, result ?? {}, () => printJson(result ?? {}));
|
|
10986
11226
|
});
|
|
10987
11227
|
popups.command("update").summary("Update a pop-up").description(
|
|
10988
|
-
"Partial update of a pop-up: each section sent merges into the current one (copy, content, design, targeting, schedule field by field; a trigger rule, the page list and the frequency are replaced whole). An active pop-up must keep its title, button and form (400 popup-incomplete). Live sites pick changes up within a few minutes."
|
|
11228
|
+
"Partial update of a pop-up: each section sent merges into the current one (copy, content, design, targeting, schedule field by field; a trigger rule, the page list and the frequency are replaced whole; another content kind replaces the content). An active pop-up must keep its title, button and its form, offer or link (400 popup-incomplete). Live sites pick changes up within a few minutes."
|
|
10989
11229
|
).argument("<id>", "Pop-up id (`popup_\u2026`) from list-popups.").option("--name <value>", "Internal name (panel only).").option(
|
|
10990
11230
|
"--priority <n>",
|
|
10991
11231
|
"Lower shows first when several pop-ups match a page; default = after the others.",
|
|
@@ -11001,7 +11241,7 @@ function registerPopupsCommands(program2) {
|
|
|
11001
11241
|
parseJsonObjectFileOrInline("--copy")
|
|
11002
11242
|
).option(
|
|
11003
11243
|
"--content <json|file>",
|
|
11004
|
-
"What the pop-up holds: a signup form.",
|
|
11244
|
+
"What the pop-up holds: a signup form, an offer (koszyk) or a link button.",
|
|
11005
11245
|
parseJsonObjectFileOrInline("--content")
|
|
11006
11246
|
).option("--design <json|file>", "design", parseJsonObjectFileOrInline("--design")).option(
|
|
11007
11247
|
"--trigger <json|file>",
|
|
@@ -11075,6 +11315,318 @@ function registerPopupsCommands(program2) {
|
|
|
11075
11315
|
const result = await client.call("DELETE", "/popups/:popupId", params, void 0, void 0);
|
|
11076
11316
|
emit(flags, result ?? {}, () => printJson(result ?? {}));
|
|
11077
11317
|
});
|
|
11318
|
+
popups.command("stats").summary("Pop-up stats").description(
|
|
11319
|
+
"Views, closes, conversions and conversion % of one pop-up, day by day for the range (default the last 30 days), and split by device and page (`{surface}~{pageId}`) for the months the range touches. A conversion is a first signup through the form, or a click on the offer or link button."
|
|
11320
|
+
).argument("<id>", "Pop-up id (`popup_\u2026`) from list-popups.").option(
|
|
11321
|
+
"--from <value>",
|
|
11322
|
+
"First day `YYYY-MM-DD` (UTC, inclusive); default 29 days before `to`."
|
|
11323
|
+
).option(
|
|
11324
|
+
"--to <value>",
|
|
11325
|
+
"Last day `YYYY-MM-DD` (UTC, inclusive); default today. At most 366 days."
|
|
11326
|
+
).action(async (id, options, command) => {
|
|
11327
|
+
const flags = getGlobalFlags(command);
|
|
11328
|
+
const client = createApiClient({ apiKey: flags.apiKey, verbose: flags.verbose });
|
|
11329
|
+
const params = {
|
|
11330
|
+
popupId: id
|
|
11331
|
+
};
|
|
11332
|
+
const payload = {
|
|
11333
|
+
from: options.from,
|
|
11334
|
+
to: options.to
|
|
11335
|
+
};
|
|
11336
|
+
const result = await client.call("GET", "/popups/:popupId/stats", params, payload, void 0);
|
|
11337
|
+
emit(flags, result ?? {}, () => printJson(result ?? {}));
|
|
11338
|
+
});
|
|
11339
|
+
}
|
|
11340
|
+
|
|
11341
|
+
// src/commands/_generated/studio.ts
|
|
11342
|
+
var suggestStudioVideoClipsColumns = ["startSeconds", "endSeconds", "hook"];
|
|
11343
|
+
function registerStudioCommands(program2) {
|
|
11344
|
+
const studio = program2.commands.find((command) => command.name() === "studio") ?? program2.command("studio");
|
|
11345
|
+
if (!studio.description()) {
|
|
11346
|
+
studio.description(
|
|
11347
|
+
"Studio videos: suggest clips, write a reel storyboard, voice it, export and download"
|
|
11348
|
+
);
|
|
11349
|
+
}
|
|
11350
|
+
const studioVideo = studio.command("video");
|
|
11351
|
+
studioVideo.description(
|
|
11352
|
+
"Studio videos: suggest clips, write a reel storyboard, voice it, export and download"
|
|
11353
|
+
);
|
|
11354
|
+
studioVideo.command("quote").summary("Quote what a Studio video costs").description(
|
|
11355
|
+
"Before spending anything: the AI credits of each Studio video step (clip suggestion, storyboard, voice-over for a reel of `lengthSeconds`), priced as the panel prices them, and the plan's monthly video exports left (the render costs no credits but uses one). Calls no model. Read-only."
|
|
11356
|
+
).option(
|
|
11357
|
+
"--length-seconds <n>",
|
|
11358
|
+
"Reel length in seconds of voice: 15, 30 or 45. Default 30.",
|
|
11359
|
+
parseNumberOption("--length-seconds", NumberFormat.Integer)
|
|
11360
|
+
).action(async (options, command) => {
|
|
11361
|
+
const flags = getGlobalFlags(command);
|
|
11362
|
+
const client = createApiClient({ apiKey: flags.apiKey, verbose: flags.verbose });
|
|
11363
|
+
const params = {};
|
|
11364
|
+
const payload = {
|
|
11365
|
+
lengthSeconds: options.lengthSeconds
|
|
11366
|
+
};
|
|
11367
|
+
const result = await client.call("GET", "/studio/videos/quote", params, payload, void 0);
|
|
11368
|
+
emit(flags, result ?? {}, () => printJson(result ?? {}));
|
|
11369
|
+
});
|
|
11370
|
+
studioVideo.command("suggest").summary("Suggest reel clips from a video transcript").description(
|
|
11371
|
+
"Reads the transcript of one library video and proposes short clips (hook, start / end seconds, why it works) for a MomentClip export. Spends AI credits (see get-studio-video-quote `suggestClipsCredits`; 402 ai-credits-exhausted when they are out). Stores nothing. The video must be transcribed first (400 clip-suggestion-transcript-unavailable)."
|
|
11372
|
+
).argument(
|
|
11373
|
+
"<mediaId>",
|
|
11374
|
+
"A transcribed video: a lesson, a LIVE recording or any library video (a media-library id from list-media)."
|
|
11375
|
+
).option(
|
|
11376
|
+
"--language <value>",
|
|
11377
|
+
"Language of the text, e.g. `pl` or `en`. Omitted: the transcript language, else `pl`."
|
|
11378
|
+
).action(async (mediaId, options, command) => {
|
|
11379
|
+
const flags = getGlobalFlags(command);
|
|
11380
|
+
const client = createApiClient({ apiKey: flags.apiKey, verbose: flags.verbose });
|
|
11381
|
+
const params = {};
|
|
11382
|
+
const payload = {
|
|
11383
|
+
mediaId,
|
|
11384
|
+
language: options.language
|
|
11385
|
+
};
|
|
11386
|
+
const result = await client.call(
|
|
11387
|
+
"POST",
|
|
11388
|
+
"/studio/videos/clip-suggestions",
|
|
11389
|
+
params,
|
|
11390
|
+
void 0,
|
|
11391
|
+
payload
|
|
11392
|
+
);
|
|
11393
|
+
const rows = tableRowsOf(result, "clips");
|
|
11394
|
+
emit(flags, result ?? {}, () => printTable(rows, suggestStudioVideoClipsColumns));
|
|
11395
|
+
});
|
|
11396
|
+
studioVideo.command("storyboard").summary("Write a reel storyboard (ScriptReel)").description(
|
|
11397
|
+
"\u201ENapisz scenariusz\u201D of \u201EWygeneruj rolk\u0119\u201D: 3-6 scenes (on-screen text + what the voice says) for a reel of `lengthSeconds`, about `topic` or grounded in a transcribed video. Spends AI credits (get-studio-video-quote `storyboardCredits`; 402 ai-credits-exhausted). Stores nothing: pass the scenes to generate-studio-video-voiceover and export-studio-video. 422 script-reel-storyboard-failed = the model answered unusably (free): try again."
|
|
11398
|
+
).option("--topic <value>", "What the reel is about; required unless `sourceMediaId`.").option(
|
|
11399
|
+
"--source-media-id <value>",
|
|
11400
|
+
"A transcribed library video (lesson, LIVE) whose transcript grounds the script."
|
|
11401
|
+
).option(
|
|
11402
|
+
"--length-seconds <n>",
|
|
11403
|
+
"Reel length in seconds of voice: 15, 30 or 45. Default 30.",
|
|
11404
|
+
parseNumberOption("--length-seconds", NumberFormat.Integer)
|
|
11405
|
+
).option(
|
|
11406
|
+
"--language <value>",
|
|
11407
|
+
"Language of the text, e.g. `pl` or `en`. Omitted: the transcript language, else `pl`."
|
|
11408
|
+
).action(async (options, command) => {
|
|
11409
|
+
const flags = getGlobalFlags(command);
|
|
11410
|
+
const client = createApiClient({ apiKey: flags.apiKey, verbose: flags.verbose });
|
|
11411
|
+
const params = {};
|
|
11412
|
+
const payload = {
|
|
11413
|
+
topic: options.topic,
|
|
11414
|
+
sourceMediaId: options.sourceMediaId,
|
|
11415
|
+
lengthSeconds: options.lengthSeconds,
|
|
11416
|
+
language: options.language
|
|
11417
|
+
};
|
|
11418
|
+
const result = await client.call(
|
|
11419
|
+
"POST",
|
|
11420
|
+
"/studio/videos/storyboards",
|
|
11421
|
+
params,
|
|
11422
|
+
void 0,
|
|
11423
|
+
payload
|
|
11424
|
+
);
|
|
11425
|
+
emit(flags, result ?? {}, () => printJson(result ?? {}));
|
|
11426
|
+
});
|
|
11427
|
+
studioVideo.command("voiceover").summary("Generate the voice-over of a reel storyboard").description(
|
|
11428
|
+
"\u201EWygeneruj g\u0142os\u201D for a storyboard: ONE voice reading every scene's voice text, stored as an Audio item in the media library. AI credits per second of the voice, charged only once it is stored (any failure is free); 402 ai-credits-exhausted before anything is made. Pass the answer's `mediaId` as `voiceoverMediaId` to export-studio-video with the SAME scenes (changed scenes = 409 script-reel-voice-mismatch)."
|
|
11429
|
+
).requiredOption(
|
|
11430
|
+
"--scenes <json|file>",
|
|
11431
|
+
"The storyboard scenes from write-studio-video-storyboard (edited or not), in order. The voice and the export must use the SAME scenes.",
|
|
11432
|
+
parseJsonArrayFileOrInline("--scenes")
|
|
11433
|
+
).addOption(
|
|
11434
|
+
new Option(
|
|
11435
|
+
"--voice-id <asia|iwona|pawel|piotr>",
|
|
11436
|
+
"Voice: asia, iwona (female), pawel, piotr (male). Default asia."
|
|
11437
|
+
).choices(["asia", "iwona", "pawel", "piotr"])
|
|
11438
|
+
).option(
|
|
11439
|
+
"--language <value>",
|
|
11440
|
+
"Language of the text, e.g. `pl` or `en`. Omitted: the transcript language, else `pl`."
|
|
11441
|
+
).option(
|
|
11442
|
+
"--request-id <value>",
|
|
11443
|
+
"Idempotency key (a uuid): a retry with the same one returns the same voice, charged once. Omitted = a new voice every call."
|
|
11444
|
+
).action(async (options, command) => {
|
|
11445
|
+
const flags = getGlobalFlags(command);
|
|
11446
|
+
const client = createApiClient({ apiKey: flags.apiKey, verbose: flags.verbose });
|
|
11447
|
+
const params = {};
|
|
11448
|
+
const payload = {
|
|
11449
|
+
scenes: options.scenes,
|
|
11450
|
+
voiceId: options.voiceId,
|
|
11451
|
+
language: options.language,
|
|
11452
|
+
requestId: options.requestId
|
|
11453
|
+
};
|
|
11454
|
+
const result = await client.call(
|
|
11455
|
+
"POST",
|
|
11456
|
+
"/studio/videos/voiceovers",
|
|
11457
|
+
params,
|
|
11458
|
+
void 0,
|
|
11459
|
+
payload
|
|
11460
|
+
);
|
|
11461
|
+
emit(flags, result ?? {}, () => printJson(result ?? {}));
|
|
11462
|
+
});
|
|
11463
|
+
studioVideo.command("export").summary("Export (render) a Studio video").description(
|
|
11464
|
+
"Starts the render of a MomentClip, ScriptReel, Montage or CoursePromo video, the same render the panel runs; the MP4 lands in the media library. No AI credits, but it uses one of the plan's monthly video exports (429 promo-video-quota-exceeded when none are left; get-studio-video-quote `exports`). Answers a job id at once (202): poll get-studio-video-status. Publishes nothing."
|
|
11465
|
+
).addOption(
|
|
11466
|
+
new Option(
|
|
11467
|
+
"--template-id <MomentClip|ScriptReel|Montage|CoursePromo>",
|
|
11468
|
+
"MomentClip = a captioned clip of a transcribed video (`moment`); ScriptReel = the storyboard reel (`scenes` + `voiceoverMediaId`); Montage = library pictures / videos (`montageScenes`); CoursePromo = a course promo (`productId`)."
|
|
11469
|
+
).choices(["MomentClip", "ScriptReel", "Montage", "CoursePromo"]).makeOptionMandatory()
|
|
11470
|
+
).option(
|
|
11471
|
+
"--moment <json|file>",
|
|
11472
|
+
"MomentClip: the segment, from suggest-studio-video-clips or chosen by hand.",
|
|
11473
|
+
parseJsonObjectFileOrInline("--moment")
|
|
11474
|
+
).option(
|
|
11475
|
+
"--scenes <json|file>",
|
|
11476
|
+
"ScriptReel: the SAME scenes the voice was generated for.",
|
|
11477
|
+
parseJsonArrayFileOrInline("--scenes")
|
|
11478
|
+
).option(
|
|
11479
|
+
"--voiceover-media-id <value>",
|
|
11480
|
+
"ScriptReel: `mediaId` from generate-studio-video-voiceover."
|
|
11481
|
+
).option(
|
|
11482
|
+
"--montage-scenes <json|file>",
|
|
11483
|
+
"Montage: 1-12 library images / videos in order.",
|
|
11484
|
+
parseJsonArrayFileOrInline("--montage-scenes")
|
|
11485
|
+
).option("--product-id <value>", "CoursePromo: the course (`prod_\u2026` from list-products).").addOption(new Option("--locale <en|pl>", "locale").choices(["en", "pl"])).addOption(
|
|
11486
|
+
new Option("--style <midnight|editorial|bold>", "style").choices(["midnight", "editorial", "bold"])
|
|
11487
|
+
).option(
|
|
11488
|
+
"--music-track-id <value>",
|
|
11489
|
+
"A music track id from the panel manifest; omitted = no music."
|
|
11490
|
+
).addOption(
|
|
11491
|
+
new Option(
|
|
11492
|
+
"--format <9:16|1:1|16:9>",
|
|
11493
|
+
"Aspect ratio; default 9:16 (MomentClip / ScriptReel are always 9:16)."
|
|
11494
|
+
).choices(["9:16", "1:1", "16:9"])
|
|
11495
|
+
).option("--cta-label <value>", "ctaLabel").option("--include-cover-image", "includeCoverImage").option("--no-include-cover-image", "Send `includeCoverImage: false` explicitly.").option("--include-social-proof", "includeSocialProof").option("--no-include-social-proof", "Send `includeSocialProof: false` explicitly.").option("--include-testimonial", "includeTestimonial").option("--no-include-testimonial", "Send `includeTestimonial: false` explicitly.").option("--include-logo", "includeLogo").option("--no-include-logo", "Send `includeLogo: false` explicitly.").action(async (options, command) => {
|
|
11496
|
+
const flags = getGlobalFlags(command);
|
|
11497
|
+
const client = createApiClient({ apiKey: flags.apiKey, verbose: flags.verbose });
|
|
11498
|
+
const params = {};
|
|
11499
|
+
const payload = {
|
|
11500
|
+
templateId: options.templateId,
|
|
11501
|
+
moment: options.moment,
|
|
11502
|
+
scenes: options.scenes,
|
|
11503
|
+
voiceoverMediaId: options.voiceoverMediaId,
|
|
11504
|
+
montageScenes: options.montageScenes,
|
|
11505
|
+
productId: options.productId,
|
|
11506
|
+
locale: options.locale,
|
|
11507
|
+
style: options.style,
|
|
11508
|
+
musicTrackId: options.musicTrackId,
|
|
11509
|
+
format: options.format,
|
|
11510
|
+
ctaLabel: options.ctaLabel,
|
|
11511
|
+
includeCoverImage: options.includeCoverImage,
|
|
11512
|
+
includeSocialProof: options.includeSocialProof,
|
|
11513
|
+
includeTestimonial: options.includeTestimonial,
|
|
11514
|
+
includeLogo: options.includeLogo
|
|
11515
|
+
};
|
|
11516
|
+
const result = await client.call(
|
|
11517
|
+
"POST",
|
|
11518
|
+
"/studio/videos/exports",
|
|
11519
|
+
params,
|
|
11520
|
+
void 0,
|
|
11521
|
+
payload
|
|
11522
|
+
);
|
|
11523
|
+
emit(flags, result ?? {}, () => printJson(result ?? {}));
|
|
11524
|
+
});
|
|
11525
|
+
studioVideo.command("status").summary("Read the status of a Studio video export").description(
|
|
11526
|
+
"The render status of an export-studio-video job; once done, the media-library item it landed (its id attaches to a social post draft via `mediaIds`) and, once the video encoded, a signed MP4 download link valid for 24 hours (ask again for a fresh one). Poll every 15-30 s; a render takes a minute or a few. Read-only."
|
|
11527
|
+
).argument("<id>", "The job id export-studio-video answered.").action(async (id, _options, command) => {
|
|
11528
|
+
const flags = getGlobalFlags(command);
|
|
11529
|
+
const client = createApiClient({ apiKey: flags.apiKey, verbose: flags.verbose });
|
|
11530
|
+
const params = {
|
|
11531
|
+
jobId: id
|
|
11532
|
+
};
|
|
11533
|
+
const result = await client.call(
|
|
11534
|
+
"GET",
|
|
11535
|
+
"/studio/videos/exports/:jobId",
|
|
11536
|
+
params,
|
|
11537
|
+
void 0,
|
|
11538
|
+
void 0
|
|
11539
|
+
);
|
|
11540
|
+
emit(flags, result ?? {}, () => printJson(result ?? {}));
|
|
11541
|
+
});
|
|
11542
|
+
}
|
|
11543
|
+
|
|
11544
|
+
// src/commands/_generated/site.ts
|
|
11545
|
+
var listSiteTemplatePagesColumns = ["pageId", "title", "slug", "issue", "nav"];
|
|
11546
|
+
var getSiteMenuColumns = ["label", "style", "href"];
|
|
11547
|
+
function registerSiteCommands(program2) {
|
|
11548
|
+
const site = program2.commands.find((command) => command.name() === "site") ?? program2.command("site");
|
|
11549
|
+
if (!site.description()) {
|
|
11550
|
+
site.description(
|
|
11551
|
+
"What frames the blog (Your site \u2192 Look and menu): the storefront shell or one of your pages; checked by the same rules as the dashboard"
|
|
11552
|
+
);
|
|
11553
|
+
}
|
|
11554
|
+
const siteLayout = site.command("layout");
|
|
11555
|
+
siteLayout.description(
|
|
11556
|
+
"What frames the blog (Your site \u2192 Look and menu): the storefront shell or one of your pages; checked by the same rules as the dashboard"
|
|
11557
|
+
);
|
|
11558
|
+
siteLayout.command("get").summary("Get the site layout").description(
|
|
11559
|
+
'What frames the blog and the shared site menu (dashboard: Your site \u2192 Look and menu). `template` = `{ kind: "storefront" }` (the storefront shell, the default) or `{ kind: "page", pageId }` (the blog wears that page\'s header, menu and footer: \u201EJak moja strona\u201D). `menu` = the shared menu in order, each entry with `href`, the address it leads to today (null = it leads nowhere now, pages leave it out); an empty menu = every page keeps its own links.'
|
|
11560
|
+
).action(async (_options, command) => {
|
|
11561
|
+
const flags = getGlobalFlags(command);
|
|
11562
|
+
const client = createApiClient({ apiKey: flags.apiKey, verbose: flags.verbose });
|
|
11563
|
+
const params = {};
|
|
11564
|
+
const result = await client.call("GET", "/site/layout", params, void 0, void 0);
|
|
11565
|
+
emit(flags, result ?? {}, () => printJson(result ?? {}));
|
|
11566
|
+
});
|
|
11567
|
+
siteLayout.command("pages").summary("List pages that can frame the blog").description(
|
|
11568
|
+
"The workspace's published pages as candidates for the blog's frame, eligible ones first. `issue` null = the page can frame the blog (pass its `pageId` to set-site-template); otherwise why not: `not-published`, `gated` (behind a product or a deadline), `custom-scripts` (its own JS), `no-header-or-footer`. `nav` = whether the shared menu shows on the page: `shared`, `not-marked` (no `data-zanfia-nav`, its own links stay) or `unsupported`."
|
|
11569
|
+
).action(async (_options, command) => {
|
|
11570
|
+
const flags = getGlobalFlags(command);
|
|
11571
|
+
const client = createApiClient({ apiKey: flags.apiKey, verbose: flags.verbose });
|
|
11572
|
+
const params = {};
|
|
11573
|
+
const result = await client.call(
|
|
11574
|
+
"GET",
|
|
11575
|
+
"/site/layout/template-pages",
|
|
11576
|
+
params,
|
|
11577
|
+
void 0,
|
|
11578
|
+
void 0
|
|
11579
|
+
);
|
|
11580
|
+
const rows = tableRowsOf(result, "pages");
|
|
11581
|
+
emit(flags, result ?? {}, () => printTable(rows, listSiteTemplatePagesColumns));
|
|
11582
|
+
});
|
|
11583
|
+
siteLayout.command("set").summary("Set what frames the blog").description(
|
|
11584
|
+
"Sets what frames the blog (\u201EWygl\u0105d bloga\u201D): `page` + `pageId` = the blog wears that page's header, menu, footer, fonts and colours (\u201EJak moja strona\u201D); `storefront` = the storefront shell. Live at once; the shared menu stays as it is. The page must be published, open to everyone, without its own scripts and with a <header> and a <footer>: 400 `site-template-page-ineligible` with `issue` otherwise, 404 `site-template-page-not-found`, 400 `site-template-page-required` for `page` without `pageId`; 403 `feature_not_available` while the blog is not on the plan. Returns the whole layout."
|
|
11585
|
+
).argument(
|
|
11586
|
+
"<kind>",
|
|
11587
|
+
"`storefront` = the storefront shell; `page` = one of your pages (`pageId`)."
|
|
11588
|
+
).option(
|
|
11589
|
+
"--page <value>",
|
|
11590
|
+
"With `kind: page`: the page that frames the blog (from list-site-template-pages)."
|
|
11591
|
+
).action(async (kind, options, command) => {
|
|
11592
|
+
const flags = getGlobalFlags(command);
|
|
11593
|
+
const client = createApiClient({ apiKey: flags.apiKey, verbose: flags.verbose });
|
|
11594
|
+
const params = {};
|
|
11595
|
+
const payload = {
|
|
11596
|
+
kind,
|
|
11597
|
+
pageId: options.page
|
|
11598
|
+
};
|
|
11599
|
+
const result = await client.call("PUT", "/site/layout/template", params, void 0, payload);
|
|
11600
|
+
emit(flags, result ?? {}, () => printJson(result ?? {}));
|
|
11601
|
+
});
|
|
11602
|
+
const siteMenu = site.command("menu");
|
|
11603
|
+
siteMenu.description("The site's one shared menu (Your site \u2192 Look and menu): up to 8 entries");
|
|
11604
|
+
siteMenu.command("get").summary("Get the shared site menu").description(
|
|
11605
|
+
"The site's shared menu (dashboard: Your site \u2192 Look and menu \u2192 \u201EMenu witryny\u201D) in order: `label`, `style` (`link` / `button`), `target` (`page` + `pageId`, `blog`, `store`, `product` + `productId`, `login`, `url` + `url`) and `href`, the address it leads to today (null = nowhere now; pages leave it out). It fills every page that marks its menu with `data-zanfia-nav` and the blog in its page frame. Empty = every page keeps its own links. Edit it with set-site-menu (the whole list)."
|
|
11606
|
+
).action(async (_options, command) => {
|
|
11607
|
+
const flags = getGlobalFlags(command);
|
|
11608
|
+
const client = createApiClient({ apiKey: flags.apiKey, verbose: flags.verbose });
|
|
11609
|
+
const params = {};
|
|
11610
|
+
const result = await client.call("GET", "/site/menu", params, void 0, void 0);
|
|
11611
|
+
const rows = tableRowsOf(result, "menu");
|
|
11612
|
+
emit(flags, result ?? {}, () => printTable(rows, getSiteMenuColumns));
|
|
11613
|
+
});
|
|
11614
|
+
siteMenu.command("set").summary("Set the shared site menu").description(
|
|
11615
|
+
'Replaces the site\'s shared menu with the entries sent, in order (up to 8; an entry left out is removed, an empty list turns the menu off). Live at once on every page that marks its menu with `data-zanfia-nav` and on the blog in its page frame. To add one entry (e.g. the blog: `{ label: "Blog", target: { kind: "blog" }, style: "link" }`) read get-site-menu first and send the whole list. Checked by the dashboard rules: 400 `site-menu-refused` with `issue` (`too-many-entries`, `empty-label`, `label-too-long`, `invalid-url`, `invalid-anchor`, `page-not-found`, `page-not-published`, `product-not-found`) and the entry `index`; nothing saved. Returns the whole layout.'
|
|
11616
|
+
).requiredOption(
|
|
11617
|
+
"--menu <json|file>",
|
|
11618
|
+
"EVERY entry of the new menu, in order (up to 8), as a JSON array; an empty list turns the shared menu off.",
|
|
11619
|
+
parseJsonArrayFileOrInline("--menu")
|
|
11620
|
+
).action(async (options, command) => {
|
|
11621
|
+
const flags = getGlobalFlags(command);
|
|
11622
|
+
const client = createApiClient({ apiKey: flags.apiKey, verbose: flags.verbose });
|
|
11623
|
+
const params = {};
|
|
11624
|
+
const payload = {
|
|
11625
|
+
menu: options.menu
|
|
11626
|
+
};
|
|
11627
|
+
const result = await client.call("PUT", "/site/menu", params, void 0, payload);
|
|
11628
|
+
emit(flags, result ?? {}, () => printJson(result ?? {}));
|
|
11629
|
+
});
|
|
11078
11630
|
}
|
|
11079
11631
|
|
|
11080
11632
|
// src/commands/_generated/index.ts
|
|
@@ -11083,6 +11635,8 @@ function registerGeneratedCommands(program2) {
|
|
|
11083
11635
|
registerBlogCommands(program2);
|
|
11084
11636
|
registerSiteRedirectsCommands(program2);
|
|
11085
11637
|
registerPopupsCommands(program2);
|
|
11638
|
+
registerStudioCommands(program2);
|
|
11639
|
+
registerSiteCommands(program2);
|
|
11086
11640
|
}
|
|
11087
11641
|
|
|
11088
11642
|
// src/commands/ads/index.ts
|
|
@@ -11397,18 +11951,18 @@ function buildAdUpdate(options) {
|
|
|
11397
11951
|
}
|
|
11398
11952
|
return input;
|
|
11399
11953
|
}
|
|
11400
|
-
var CONNECTION_ARG = [
|
|
11401
|
-
|
|
11402
|
-
"
|
|
11954
|
+
var CONNECTION_ARG = ["<connectionId>", "Meta connection id (from `ads accounts`)"];
|
|
11955
|
+
var AD_ACCOUNT_ARG = [
|
|
11956
|
+
"<adAccountId>",
|
|
11957
|
+
"Meta ad account id (`act_\u2026`, from `ads accounts`)"
|
|
11403
11958
|
];
|
|
11404
|
-
var AD_ACCOUNT_ARG = ["<adAccountId>", "Meta ad account id (`act_\u2026`)"];
|
|
11405
11959
|
var JSON_FLAG_HINT = "(inline JSON or a .json path; JSON output for this command: -o json or the root --json)";
|
|
11406
11960
|
function registerAdsCommands(program2) {
|
|
11407
11961
|
const ads = program2.command("ads").description(
|
|
11408
11962
|
"Meta Ads \u2014 spend vs. Zanfia revenue per ad, and campaign / ad set / ad / creative management on connected ad accounts"
|
|
11409
11963
|
);
|
|
11410
11964
|
ads.command("performance").description(
|
|
11411
|
-
"Spend per ad (ingested insights) joined with Zanfia orders captured via UTM ad ids, plus a per-day spend / revenue series"
|
|
11965
|
+
"Spend per ad (ingested insights) joined with Zanfia orders captured via UTM ad ids (last click) and with orders of buyers who first came from the ad (first touch), plus a per-day spend / revenue series"
|
|
11412
11966
|
).option("--from <date>", "inclusive UTC start, YYYY-MM-DD (server default when omitted)").option("--to <date>", "inclusive UTC end, YYYY-MM-DD (server default when omitted)").option(
|
|
11413
11967
|
"--checkout <checkoutId>",
|
|
11414
11968
|
"scope orders and spend to one checkout (spendScope becomes `checkout`)"
|
|
@@ -11419,7 +11973,7 @@ function registerAdsCommands(program2) {
|
|
|
11419
11973
|
emit(flags, result, () => {
|
|
11420
11974
|
const currency = result.rows.find((r) => r.currency)?.currency ?? null;
|
|
11421
11975
|
process.stdout.write(
|
|
11422
|
-
`Totals (${result.spendScope} scope): spend ${formatMinor(result.totals.spendMinor, currency)}, ${result.totals.orders} orders, revenue ${formatMinor(result.totals.revenueMinor, currency)}, ROAS ${formatRoas(result.totals.roas) || "n/a"}; ${result.unattributedMetaOrders} Meta orders without an ad id
|
|
11976
|
+
`Totals (${result.spendScope} scope): spend ${formatMinor(result.totals.spendMinor, currency)}, last click: ${result.totals.orders} orders, revenue ${formatMinor(result.totals.revenueMinor, currency)}, ROAS ${formatRoas(result.totals.roas) || "n/a"}; first touch: ${result.totals.firstTouchOrders ?? 0} orders, revenue ${formatMinor(result.totals.firstTouchRevenueMinor ?? 0, currency)}, ROAS ${formatRoas(result.totals.firstTouchRoas ?? null) || "n/a"}; ${result.unattributedMetaOrders} Meta orders without an ad id
|
|
11423
11977
|
|
|
11424
11978
|
`
|
|
11425
11979
|
);
|
|
@@ -11437,7 +11991,10 @@ function registerAdsCommands(program2) {
|
|
|
11437
11991
|
metaPurchases: r.metaPurchases,
|
|
11438
11992
|
orders: r.orders,
|
|
11439
11993
|
revenue: formatMinor(r.revenueMinor, r.currency),
|
|
11440
|
-
roas: formatRoas(r.roas)
|
|
11994
|
+
roas: formatRoas(r.roas),
|
|
11995
|
+
firstTouchOrders: r.firstTouchOrders ?? 0,
|
|
11996
|
+
firstTouchRevenue: formatMinor(r.firstTouchRevenueMinor ?? 0, r.currency),
|
|
11997
|
+
firstTouchRoas: formatRoas(r.firstTouchRoas ?? null)
|
|
11441
11998
|
})),
|
|
11442
11999
|
[
|
|
11443
12000
|
"adId",
|
|
@@ -11450,7 +12007,10 @@ function registerAdsCommands(program2) {
|
|
|
11450
12007
|
"metaPurchases",
|
|
11451
12008
|
"orders",
|
|
11452
12009
|
"revenue",
|
|
11453
|
-
"roas"
|
|
12010
|
+
"roas",
|
|
12011
|
+
"firstTouchOrders",
|
|
12012
|
+
"firstTouchRevenue",
|
|
12013
|
+
"firstTouchRoas"
|
|
11454
12014
|
]
|
|
11455
12015
|
);
|
|
11456
12016
|
process.stdout.write(`
|
|
@@ -11467,6 +12027,41 @@ Daily (${result.daily.length})
|
|
|
11467
12027
|
);
|
|
11468
12028
|
});
|
|
11469
12029
|
});
|
|
12030
|
+
ads.command("accounts").description(
|
|
12031
|
+
"Meta connections and the ad accounts each one reaches (live from Meta: name, currency, status) \u2014 the <connectionId> and <adAccountId> every campaign / ad set / ad command takes"
|
|
12032
|
+
).action(async (_, command) => {
|
|
12033
|
+
const flags = getGlobalFlags(command);
|
|
12034
|
+
const client = createApiClient({ apiKey: flags.apiKey, verbose: flags.verbose });
|
|
12035
|
+
const result = await client.listMetaAdConnections();
|
|
12036
|
+
emit(flags, result, () => {
|
|
12037
|
+
if (result.connections.length === 0) {
|
|
12038
|
+
process.stdout.write(
|
|
12039
|
+
"No Meta connection \u2014 connect one under Settings \u2192 Integrations \u2192 Ads.\n"
|
|
12040
|
+
);
|
|
12041
|
+
return;
|
|
12042
|
+
}
|
|
12043
|
+
printTable(
|
|
12044
|
+
result.connections.flatMap((connection) => {
|
|
12045
|
+
const head = {
|
|
12046
|
+
connectionId: connection.connectionId,
|
|
12047
|
+
connection: connection.label,
|
|
12048
|
+
issue: connection.issue ?? ""
|
|
12049
|
+
};
|
|
12050
|
+
if (connection.adAccounts.length === 0) {
|
|
12051
|
+
return [{ ...head, adAccountId: "", name: "", currency: "", status: "" }];
|
|
12052
|
+
}
|
|
12053
|
+
return connection.adAccounts.map((account) => ({
|
|
12054
|
+
...head,
|
|
12055
|
+
adAccountId: account.adAccountId,
|
|
12056
|
+
name: account.name ?? "",
|
|
12057
|
+
currency: account.currency ?? "",
|
|
12058
|
+
status: account.accountStatus ?? ""
|
|
12059
|
+
}));
|
|
12060
|
+
}),
|
|
12061
|
+
["connectionId", "connection", "adAccountId", "name", "currency", "status", "issue"]
|
|
12062
|
+
);
|
|
12063
|
+
});
|
|
12064
|
+
});
|
|
11470
12065
|
ads.command("directory").description(
|
|
11471
12066
|
"Ads known to this workspace (from the insights ingest): ad / ad set / campaign ids and names plus the destination checkout \u2014 the lookup behind `utm_content` ad ids"
|
|
11472
12067
|
).action(async (_, command) => {
|
|
@@ -11696,6 +12291,27 @@ async function runWhoami(command) {
|
|
|
11696
12291
|
const rawApiKey = command.optsWithGlobals().apiKey;
|
|
11697
12292
|
const logins = listStoredProfiles().length;
|
|
11698
12293
|
const apiKey = resolveApiKey(flags.apiKey);
|
|
12294
|
+
if (decodeTokenKind(apiKey) === "agent") {
|
|
12295
|
+
const claims = decodeTokenClaims(apiKey);
|
|
12296
|
+
const info2 = {
|
|
12297
|
+
role: "agent",
|
|
12298
|
+
workspaceId: claims.workspaceId ?? "?",
|
|
12299
|
+
agentId: claims.agentId ?? null,
|
|
12300
|
+
uid: claims.subject ?? null,
|
|
12301
|
+
communityApiUrl: resolveCommunityApiUrl()
|
|
12302
|
+
};
|
|
12303
|
+
emit(flags, info2, () => {
|
|
12304
|
+
process.stdout.write(`Role: community agent key (posts and comments go out as the agent)
|
|
12305
|
+
`);
|
|
12306
|
+
process.stdout.write(`Workspace: ${info2.workspaceId}
|
|
12307
|
+
`);
|
|
12308
|
+
process.stdout.write(`Agent: ${info2.agentId ?? "(unknown)"}
|
|
12309
|
+
`);
|
|
12310
|
+
process.stdout.write(`Community API: ${info2.communityApiUrl ?? "<unresolved>"}
|
|
12311
|
+
`);
|
|
12312
|
+
});
|
|
12313
|
+
return;
|
|
12314
|
+
}
|
|
11699
12315
|
if (decodeTokenKind(apiKey) === "member") {
|
|
11700
12316
|
const stored = readCredentials();
|
|
11701
12317
|
const claims = decodeTokenClaims(apiKey);
|
|
@@ -11849,9 +12465,15 @@ Resolved by next call:
|
|
|
11849
12465
|
source: "manual",
|
|
11850
12466
|
kind
|
|
11851
12467
|
});
|
|
11852
|
-
const
|
|
12468
|
+
const memberLabel = isReadOnlyMemberKey(claims) ? "read-only member " : "member ";
|
|
12469
|
+
const label = kind === "member" ? memberLabel : kind === "agent" ? "agent " : "";
|
|
11853
12470
|
process.stdout.write(`Saved ${label}API key to ${path2}
|
|
11854
12471
|
`);
|
|
12472
|
+
if (kind === "agent") {
|
|
12473
|
+
process.stdout.write(
|
|
12474
|
+
"Note: an agent key only reaches the Community API (`zanfia communities \u2026`); posts and comments go out as the agent.\n"
|
|
12475
|
+
);
|
|
12476
|
+
}
|
|
11855
12477
|
if (kind === "member" && !options.apiUrl) {
|
|
11856
12478
|
process.stdout.write(
|
|
11857
12479
|
"Note: member keys only reach the Community API. Pass --api-url <api-community-url> so `zanfia communities \u2026` knows where to call.\n"
|
|
@@ -11872,18 +12494,18 @@ import { platform } from "os";
|
|
|
11872
12494
|
async function openBrowser(url) {
|
|
11873
12495
|
const cmd = browserOpenCommand(url);
|
|
11874
12496
|
if (!cmd) return false;
|
|
11875
|
-
return new Promise((
|
|
12497
|
+
return new Promise((resolve3) => {
|
|
11876
12498
|
try {
|
|
11877
12499
|
const child = spawn(cmd.command, cmd.args, {
|
|
11878
12500
|
detached: true,
|
|
11879
12501
|
stdio: "ignore",
|
|
11880
12502
|
shell: cmd.shell
|
|
11881
12503
|
});
|
|
11882
|
-
child.on("error", () =>
|
|
12504
|
+
child.on("error", () => resolve3(false));
|
|
11883
12505
|
child.unref();
|
|
11884
|
-
setTimeout(() =>
|
|
12506
|
+
setTimeout(() => resolve3(true), 100);
|
|
11885
12507
|
} catch {
|
|
11886
|
-
|
|
12508
|
+
resolve3(false);
|
|
11887
12509
|
}
|
|
11888
12510
|
});
|
|
11889
12511
|
}
|
|
@@ -11979,11 +12601,11 @@ async function startLoopbackServer() {
|
|
|
11979
12601
|
);
|
|
11980
12602
|
resolveCallback?.({ code, state });
|
|
11981
12603
|
});
|
|
11982
|
-
await new Promise((
|
|
12604
|
+
await new Promise((resolve3, reject) => {
|
|
11983
12605
|
server.once("error", reject);
|
|
11984
12606
|
server.listen(0, "127.0.0.1", () => {
|
|
11985
12607
|
server.removeListener("error", reject);
|
|
11986
|
-
|
|
12608
|
+
resolve3();
|
|
11987
12609
|
});
|
|
11988
12610
|
});
|
|
11989
12611
|
const address = server.address();
|
|
@@ -11991,13 +12613,13 @@ async function startLoopbackServer() {
|
|
|
11991
12613
|
server.close();
|
|
11992
12614
|
throw new Error("Could not bind loopback server.");
|
|
11993
12615
|
}
|
|
11994
|
-
const awaitCallback = (expectedState) => new Promise((
|
|
12616
|
+
const awaitCallback = (expectedState) => new Promise((resolve3, reject) => {
|
|
11995
12617
|
resolveCallback = (result) => {
|
|
11996
12618
|
if (result.state !== expectedState) {
|
|
11997
12619
|
reject(new Error("State mismatch \u2014 possible CSRF. Aborting."));
|
|
11998
12620
|
return;
|
|
11999
12621
|
}
|
|
12000
|
-
|
|
12622
|
+
resolve3(result);
|
|
12001
12623
|
};
|
|
12002
12624
|
rejectCallback = reject;
|
|
12003
12625
|
setTimeout(() => reject(new Error("Login timed out.")), CALLBACK_TIMEOUT_MS).unref();
|
|
@@ -12359,11 +12981,14 @@ import { hostname as hostname2 } from "os";
|
|
|
12359
12981
|
var HttpJsonError = class extends Error {
|
|
12360
12982
|
status;
|
|
12361
12983
|
code;
|
|
12362
|
-
|
|
12984
|
+
/** The server's own message, without the `<label> failed (<status>):` prefix. */
|
|
12985
|
+
serverMessage;
|
|
12986
|
+
constructor(message, status, code, serverMessage = message) {
|
|
12363
12987
|
super(message);
|
|
12364
12988
|
this.name = "HttpJsonError";
|
|
12365
12989
|
this.status = status;
|
|
12366
12990
|
this.code = code;
|
|
12991
|
+
this.serverMessage = serverMessage;
|
|
12367
12992
|
}
|
|
12368
12993
|
};
|
|
12369
12994
|
async function postJson2(label, url, body, options = {}) {
|
|
@@ -12381,10 +13006,12 @@ async function postJson2(label, url, body, options = {}) {
|
|
|
12381
13006
|
});
|
|
12382
13007
|
const text = await response.text();
|
|
12383
13008
|
if (!response.ok) {
|
|
13009
|
+
const serverMessage = extractErrorMessage(text, response.statusText);
|
|
12384
13010
|
throw new HttpJsonError(
|
|
12385
|
-
`${label} failed (${response.status}): ${
|
|
13011
|
+
`${label} failed (${response.status}): ${serverMessage}`,
|
|
12386
13012
|
response.status,
|
|
12387
|
-
extractErrorCode(text)
|
|
13013
|
+
extractErrorCode(text),
|
|
13014
|
+
serverMessage
|
|
12388
13015
|
);
|
|
12389
13016
|
}
|
|
12390
13017
|
return JSON.parse(text);
|
|
@@ -12431,7 +13058,7 @@ async function promptLine(question) {
|
|
|
12431
13058
|
function promptHidden(question) {
|
|
12432
13059
|
const { stdin, stdout } = process;
|
|
12433
13060
|
if (!stdin.isTTY) return promptLine(question);
|
|
12434
|
-
return new Promise((
|
|
13061
|
+
return new Promise((resolve3) => {
|
|
12435
13062
|
stdout.write(question);
|
|
12436
13063
|
stdin.setRawMode(true);
|
|
12437
13064
|
stdin.resume();
|
|
@@ -12443,7 +13070,7 @@ function promptHidden(question) {
|
|
|
12443
13070
|
stdin.pause();
|
|
12444
13071
|
stdin.removeListener("data", onData);
|
|
12445
13072
|
stdout.write("\n");
|
|
12446
|
-
|
|
13073
|
+
resolve3(value);
|
|
12447
13074
|
return;
|
|
12448
13075
|
}
|
|
12449
13076
|
if (char === CTRL_C) {
|
|
@@ -12805,10 +13432,10 @@ function registerBrandCommands(program2) {
|
|
|
12805
13432
|
});
|
|
12806
13433
|
}
|
|
12807
13434
|
async function buildUpdatePatch(options) {
|
|
12808
|
-
const { readFile:
|
|
13435
|
+
const { readFile: readFile6 } = await import("fs/promises");
|
|
12809
13436
|
let patch = {};
|
|
12810
13437
|
if (options.jsonFile) {
|
|
12811
|
-
const raw = await
|
|
13438
|
+
const raw = await readFile6(options.jsonFile, "utf8");
|
|
12812
13439
|
const parsed = JSON.parse(raw);
|
|
12813
13440
|
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
12814
13441
|
throw new Error("--json-file must contain a JSON object with name/visual/editorial keys.");
|
|
@@ -13103,6 +13730,12 @@ function printCampaignDetail(campaign) {
|
|
|
13103
13730
|
}
|
|
13104
13731
|
if (campaign.completedAt) process.stdout.write(` Completed at: ${campaign.completedAt}
|
|
13105
13732
|
`);
|
|
13733
|
+
if (campaign.pacing) {
|
|
13734
|
+
process.stdout.write(
|
|
13735
|
+
` Pacing: new sending domain ${campaign.pacing.domain}, ~${campaign.pacing.hourlyCap}/h, estimated end ${campaign.pacing.estimatedEndAt}
|
|
13736
|
+
`
|
|
13737
|
+
);
|
|
13738
|
+
}
|
|
13106
13739
|
const { stats } = campaign;
|
|
13107
13740
|
process.stdout.write(
|
|
13108
13741
|
` Stats: sent ${stats.sent}, delivered ${stats.delivered}, bounced ${stats.bounced}, complained ${stats.complained}, failed ${stats.failed}, skipped ${stats.skippedSuppressed + stats.skippedMuted}
|
|
@@ -14548,7 +15181,13 @@ Next page: --cursor ${page.nextCursor}
|
|
|
14548
15181
|
});
|
|
14549
15182
|
}
|
|
14550
15183
|
);
|
|
14551
|
-
clients2.command("add").description("Add a contact-only client (no product access granted)").requiredOption("--email <email>", "client email").option("--first-name <name>", "client first name").option("--last-name <name>", "client last name").option("--phone <number>", "client phone number").option("--language <code>", "preferred language code (e.g. en, pl)").
|
|
15184
|
+
clients2.command("add").description("Add a contact-only client (no product access granted)").requiredOption("--email <email>", "client email").option("--first-name <name>", "client first name").option("--last-name <name>", "client last name").option("--phone <number>", "client phone number").option("--language <code>", "preferred language code (e.g. en, pl)").option(
|
|
15185
|
+
"--marketing-consent",
|
|
15186
|
+
"subscribe the client to marketing e-mail (needs --consent-statement unless the key gives consent)"
|
|
15187
|
+
).option(
|
|
15188
|
+
"--consent-statement",
|
|
15189
|
+
"your statement: you hold this person's marketing consent and answer for it"
|
|
15190
|
+
).action(
|
|
14552
15191
|
async (options, command) => {
|
|
14553
15192
|
const flags = getGlobalFlags(command);
|
|
14554
15193
|
const client = createApiClient({ apiKey: flags.apiKey, verbose: flags.verbose });
|
|
@@ -14557,7 +15196,9 @@ Next page: --cursor ${page.nextCursor}
|
|
|
14557
15196
|
firstName: options.firstName,
|
|
14558
15197
|
lastName: options.lastName,
|
|
14559
15198
|
phoneNumber: options.phone,
|
|
14560
|
-
language: options.language
|
|
15199
|
+
language: options.language,
|
|
15200
|
+
...options.marketingConsent ? { marketingConsent: true } : {},
|
|
15201
|
+
...options.consentStatement ? { consentStatement: true } : {}
|
|
14561
15202
|
});
|
|
14562
15203
|
emit(flags, result, () => {
|
|
14563
15204
|
process.stdout.write(
|
|
@@ -14565,6 +15206,10 @@ Next page: --cursor ${page.nextCursor}
|
|
|
14565
15206
|
` : `Client already exists: ${result.clientId} (${options.email}).
|
|
14566
15207
|
`
|
|
14567
15208
|
);
|
|
15209
|
+
if (result.marketingConsent && result.marketingConsent !== "not-requested") {
|
|
15210
|
+
process.stdout.write(`Marketing consent: ${result.marketingConsent}.
|
|
15211
|
+
`);
|
|
15212
|
+
}
|
|
14568
15213
|
});
|
|
14569
15214
|
}
|
|
14570
15215
|
);
|
|
@@ -14807,17 +15452,22 @@ Next page: --cursor ${page.nextCursor}
|
|
|
14807
15452
|
});
|
|
14808
15453
|
}
|
|
14809
15454
|
);
|
|
14810
|
-
clients2.command("revoke").description(
|
|
15455
|
+
clients2.command("revoke").description(
|
|
15456
|
+
"Revoke a client's product access (NO refund). Access whose subscription already ended in Stripe is revoked; a live subscription is refused unless --cancel-subscription"
|
|
15457
|
+
).argument("<productId>", "product id").requiredOption("--email <email>", "client email").option("--run-automations", "also run the product's access-end automation tasks").option("--cancel-subscription", "cancel a still-live subscription now, then revoke").option("--silent", "no e-mail, automations or workflows for this revoke").action(
|
|
14811
15458
|
async (productId, options, command) => {
|
|
14812
15459
|
const flags = getGlobalFlags(command);
|
|
14813
15460
|
const client = createApiClient({ apiKey: flags.apiKey, verbose: flags.verbose });
|
|
14814
15461
|
const result = await client.revokeClientAccess(productId, {
|
|
14815
15462
|
email: options.email,
|
|
14816
|
-
runAutomations: options.runAutomations
|
|
15463
|
+
runAutomations: options.runAutomations,
|
|
15464
|
+
cancelSubscription: options.cancelSubscription,
|
|
15465
|
+
silent: options.silent
|
|
14817
15466
|
});
|
|
14818
15467
|
emit(flags, result, () => {
|
|
15468
|
+
const subscriptionNote = result.subscription === "cancelled" ? " The subscription was cancelled in Stripe." : result.subscription === "already_ended" ? " The subscription had already ended in Stripe." : "";
|
|
14819
15469
|
process.stdout.write(
|
|
14820
|
-
`Revoked ${options.email}'s access to product ${productId} (no refund issued; re-grant with \`clients grant\`)
|
|
15470
|
+
`Revoked ${options.email}'s access to product ${productId} (no refund issued; re-grant with \`clients grant\`).${subscriptionNote}
|
|
14821
15471
|
`
|
|
14822
15472
|
);
|
|
14823
15473
|
});
|
|
@@ -14869,7 +15519,8 @@ Next page: --cursor ${page.nextCursor}
|
|
|
14869
15519
|
const result = await client.cancelClientSubscription(productId, { email: options.email });
|
|
14870
15520
|
emit(flags, result, () => {
|
|
14871
15521
|
process.stdout.write(
|
|
14872
|
-
|
|
15522
|
+
result.alreadyCancelled ? `${options.email}'s subscription to product ${productId} had already ended in Stripe \u2014 nothing was cancelled now; the access record was brought in line.
|
|
15523
|
+
` : `Cancelled ${options.email}'s subscription to product ${productId} \u2014 access runs to the end of the paid period (check \`clients accesses\` for the date).
|
|
14873
15524
|
`
|
|
14874
15525
|
);
|
|
14875
15526
|
});
|
|
@@ -15274,7 +15925,8 @@ function eventRow(event) {
|
|
|
15274
15925
|
endAt: event.endAt,
|
|
15275
15926
|
timezone: event.timezone,
|
|
15276
15927
|
status: event.status,
|
|
15277
|
-
location: formatEventLocation(event.location)
|
|
15928
|
+
location: formatEventLocation(event.location),
|
|
15929
|
+
rsvp: event.rsvpEnabled === false ? "off" : `${event.goingCount ?? 0} going`
|
|
15278
15930
|
};
|
|
15279
15931
|
}
|
|
15280
15932
|
var EVENT_COLUMNS = [
|
|
@@ -15285,8 +15937,19 @@ var EVENT_COLUMNS = [
|
|
|
15285
15937
|
"endAt",
|
|
15286
15938
|
"timezone",
|
|
15287
15939
|
"status",
|
|
15288
|
-
"location"
|
|
15940
|
+
"location",
|
|
15941
|
+
"rsvp"
|
|
15289
15942
|
];
|
|
15943
|
+
function attendeeRow(attendee) {
|
|
15944
|
+
return {
|
|
15945
|
+
memberId: attendee.memberId,
|
|
15946
|
+
name: [attendee.firstName, attendee.lastName].filter(Boolean).join(" "),
|
|
15947
|
+
email: attendee.email ?? "",
|
|
15948
|
+
answeredAt: attendee.respondedAt,
|
|
15949
|
+
access: attendee.hasAccess ? "yes" : "lost"
|
|
15950
|
+
};
|
|
15951
|
+
}
|
|
15952
|
+
var ATTENDEE_COLUMNS = ["memberId", "name", "email", "answeredAt", "access"];
|
|
15290
15953
|
function moderationRow(report) {
|
|
15291
15954
|
const reason = report.reporters.map((r) => r.reason).find((r) => Boolean(r)) ?? "";
|
|
15292
15955
|
return {
|
|
@@ -15942,7 +16605,10 @@ ${message.content}
|
|
|
15942
16605
|
);
|
|
15943
16606
|
communities2.command("members").description(
|
|
15944
16607
|
"List community members (staff only). Filters --role, --disabled and --email-prefix are mutually exclusive."
|
|
15945
|
-
).argument("<communityId>", "community id").option("--role <roleId>", "only members holding this role").option("--disabled <true|false>", "only disabled (soft-banned) / only active members").option(
|
|
16608
|
+
).argument("<communityId>", "community id").option("--role <roleId>", "only members holding this role").option("--disabled <true|false>", "only disabled (soft-banned) / only active members").option(
|
|
16609
|
+
"--email-prefix <prefix>",
|
|
16610
|
+
"email prefix search (case-insensitive; workspace keys only, a member key gets 403)"
|
|
16611
|
+
).option("--limit <n>", "page size (max 100)").option("--cursor <cursor>", "pagination cursor from a previous page").action(
|
|
15946
16612
|
async (communityId, options, command) => {
|
|
15947
16613
|
const flags = getGlobalFlags(command);
|
|
15948
16614
|
const filterCount = [options.role, options.disabled, options.emailPrefix].filter(
|
|
@@ -16050,8 +16716,11 @@ Next page: --cursor ${result.nextCursor}
|
|
|
16050
16716
|
});
|
|
16051
16717
|
});
|
|
16052
16718
|
settings.command("update").description(
|
|
16053
|
-
"Patch community settings \u2014 via toggle flags and/or a JSON patch. Allowed patch keys: uploadVideo, membersDisplay, viewStyle, displayName, icon, hiddenWhenUnavailable, voiceMessages, enableMemberApiAccess, publicAccess, publicAccessTermsUrl, contentVisibleFromJoinDate, eventsEnabled, directMessagesEnabled, gamification, moderation, assistant, digest. Nested blocks in the JSON patch replace their whole sub-object; the --gamification / --auto-hide-after-reports flags merge into the existing block instead."
|
|
16054
|
-
).argument("<communityId>", "community id").option("--patch <fileOrJson>", "the settings patch as inline JSON or a path to a .json file").option("--events-enabled <on|off>", "show / hide the Calendar (events) section for members").option(
|
|
16719
|
+
"Patch community settings \u2014 via toggle flags and/or a JSON patch. Allowed patch keys: uploadVideo, membersDisplay, viewStyle, displayName, icon, hiddenWhenUnavailable, voiceMessages, enableMemberApiAccess, publicAccess, publicAccessTermsUrl, contentVisibleFromJoinDate, eventsEnabled, eventAnnouncements, directMessagesEnabled, gamification, moderation, assistant, digest. Nested blocks in the JSON patch replace their whole sub-object; the --gamification / --auto-hide-after-reports flags merge into the existing block instead."
|
|
16720
|
+
).argument("<communityId>", "community id").option("--patch <fileOrJson>", "the settings patch as inline JSON or a path to a .json file").option("--events-enabled <on|off>", "show / hide the Calendar (events) section for members").option(
|
|
16721
|
+
"--event-announcements <on|off>",
|
|
16722
|
+
"bell + e-mail to members when a new event is published (default on)"
|
|
16723
|
+
).option("--direct-messages <on|off>", "allow / block member \u2194 member direct messages").option(
|
|
16055
16724
|
"--content-visible-from-join-date <on|off>",
|
|
16056
16725
|
'"archive from purchase date" \u2014 members only see posts newer than their own access start'
|
|
16057
16726
|
).option("--gamification <on|off>", "XP / levels / leaderboards (stored reputation is kept)").option(
|
|
@@ -16065,6 +16734,12 @@ Next page: --cursor ${result.nextCursor}
|
|
|
16065
16734
|
if (options.eventsEnabled !== void 0) {
|
|
16066
16735
|
patch.eventsEnabled = parseOnOff(options.eventsEnabled, "--events-enabled");
|
|
16067
16736
|
}
|
|
16737
|
+
if (options.eventAnnouncements !== void 0) {
|
|
16738
|
+
patch.eventAnnouncements = parseOnOff(
|
|
16739
|
+
options.eventAnnouncements,
|
|
16740
|
+
"--event-announcements"
|
|
16741
|
+
);
|
|
16742
|
+
}
|
|
16068
16743
|
if (options.directMessages !== void 0) {
|
|
16069
16744
|
patch.directMessagesEnabled = parseOnOff(options.directMessages, "--direct-messages");
|
|
16070
16745
|
}
|
|
@@ -16092,7 +16767,7 @@ Next page: --cursor ${result.nextCursor}
|
|
|
16092
16767
|
const keys = Object.keys(patch);
|
|
16093
16768
|
if (keys.length === 0) {
|
|
16094
16769
|
throw new Error(
|
|
16095
|
-
"Nothing to update \u2014 pass --patch and/or one of the toggle flags (--events-enabled, --direct-messages, --content-visible-from-join-date, --gamification, --auto-hide-after-reports)."
|
|
16770
|
+
"Nothing to update \u2014 pass --patch and/or one of the toggle flags (--events-enabled, --event-announcements, --direct-messages, --content-visible-from-join-date, --gamification, --auto-hide-after-reports)."
|
|
16096
16771
|
);
|
|
16097
16772
|
}
|
|
16098
16773
|
const result = await client.updateCommunitySettings(communityId, patch);
|
|
@@ -16233,9 +16908,12 @@ Next page: --cursor ${result.nextCursor}
|
|
|
16233
16908
|
);
|
|
16234
16909
|
events.command("create").description(
|
|
16235
16910
|
'Create a calendar event (staff). Flags override --json; `--location` is {"type":"online","url":\u2026} or {"type":"inPerson","address":\u2026}.'
|
|
16236
|
-
).argument("<communityId>", "community id").option("--title <title>", "event title").option("--description <text>", "plain-text description").option("--start <iso>", "start \u2014 ISO 8601 instant (e.g. 2026-09-01T17:00:00Z)").option("--end <iso>", "end \u2014 ISO 8601 instant").option("--timezone <iana>", "IANA timezone the event is authored in (e.g. Europe/Warsaw)").option("--type <kind>", `event kind: ${COMMUNITY_EVENT_KINDS.join("|")} (default other)`).option("--all-day", "all-day event (start/end span whole days, no times shown)").option("--location <fileOrJson>", "location object as inline JSON or a .json file path").option(
|
|
16911
|
+
).argument("<communityId>", "community id").option("--title <title>", "event title").option("--description <text>", "plain-text description").option("--start <iso>", "start \u2014 ISO 8601 instant (e.g. 2026-09-01T17:00:00Z)").option("--end <iso>", "end \u2014 ISO 8601 instant").option("--timezone <iana>", "IANA timezone the event is authored in (e.g. Europe/Warsaw)").option("--type <kind>", `event kind: ${COMMUNITY_EVENT_KINDS.join("|")} (default other)`).option("--all-day", "all-day event (start/end span whole days, no times shown)").option("--location <fileOrJson>", "location object as inline JSON or a .json file path").option("--rsvp <on|off>", "\u201EZbieraj zapisy\u201D: members answer going / not going (default on)").option(
|
|
16912
|
+
"--announce <on|off>",
|
|
16913
|
+
"\u201EPowiadom cz\u0142onk\xF3w\u201D: bell + one e-mail \u201ENowe wydarzenie\u201D to members (default on)"
|
|
16914
|
+
).option(
|
|
16237
16915
|
"--json <file>",
|
|
16238
|
-
"full request body from a .json file (title, description, startAt, endAt, timezone, type, allDay, location, coverImage)"
|
|
16916
|
+
"full request body from a .json file (title, description, startAt, endAt, timezone, type, allDay, location, coverImage, rsvpEnabled, announce)"
|
|
16239
16917
|
).action(
|
|
16240
16918
|
async (communityId, options, command) => {
|
|
16241
16919
|
const flags = getGlobalFlags(command);
|
|
@@ -16249,7 +16927,9 @@ Next page: --cursor ${result.nextCursor}
|
|
|
16249
16927
|
...options.timezone !== void 0 ? { timezone: options.timezone } : {},
|
|
16250
16928
|
...options.type !== void 0 ? { type: assertOneOf(options.type, COMMUNITY_EVENT_KINDS, "--type") } : {},
|
|
16251
16929
|
...options.allDay ? { allDay: true } : {},
|
|
16252
|
-
...options.location !== void 0 ? { location: parseEventLocation(options.location) } : {}
|
|
16930
|
+
...options.location !== void 0 ? { location: parseEventLocation(options.location) } : {},
|
|
16931
|
+
...options.rsvp !== void 0 ? { rsvpEnabled: parseOnOff(options.rsvp, "--rsvp") } : {},
|
|
16932
|
+
...options.announce !== void 0 ? { announce: parseOnOff(options.announce, "--announce") } : {}
|
|
16253
16933
|
};
|
|
16254
16934
|
const missing = ["title", "startAt", "endAt", "timezone", "location"].filter(
|
|
16255
16935
|
(key) => input[key] === void 0
|
|
@@ -16274,7 +16954,7 @@ Next page: --cursor ${result.nextCursor}
|
|
|
16274
16954
|
);
|
|
16275
16955
|
events.command("update").description(
|
|
16276
16956
|
"Update a calendar event (staff). Every flag is optional; `--status cancelled` cancels without deleting, `--status scheduled` restores."
|
|
16277
|
-
).argument("<communityId>", "community id").argument("<eventId>", "event id").option("--title <title>", "new title").option("--description <text>", "new plain-text description").option("--start <iso>", "new start \u2014 ISO 8601 instant").option("--end <iso>", "new end \u2014 ISO 8601 instant").option("--timezone <iana>", "new IANA timezone").option("--type <kind>", `event kind: ${COMMUNITY_EVENT_KINDS.join("|")}`).option("--all-day <on|off>", "switch the all-day mode on/off").option("--location <fileOrJson>", "new location object as inline JSON or a .json file path").option("--status <status>", `${SETTABLE_COMMUNITY_EVENT_STATUSES.join("|")}`).option("--json <file>", "patch body from a .json file (flags override)").action(
|
|
16957
|
+
).argument("<communityId>", "community id").argument("<eventId>", "event id").option("--title <title>", "new title").option("--description <text>", "new plain-text description").option("--start <iso>", "new start \u2014 ISO 8601 instant").option("--end <iso>", "new end \u2014 ISO 8601 instant").option("--timezone <iana>", "new IANA timezone").option("--type <kind>", `event kind: ${COMMUNITY_EVENT_KINDS.join("|")}`).option("--all-day <on|off>", "switch the all-day mode on/off").option("--location <fileOrJson>", "new location object as inline JSON or a .json file path").option("--status <status>", `${SETTABLE_COMMUNITY_EVENT_STATUSES.join("|")}`).option("--rsvp <on|off>", "\u201EZbieraj zapisy\u201D: collect going / not going answers or stop").option("--json <file>", "patch body from a .json file (flags override)").action(
|
|
16278
16958
|
async (communityId, eventId, options, command) => {
|
|
16279
16959
|
const flags = getGlobalFlags(command);
|
|
16280
16960
|
const base = options.json !== void 0 ? parseJsonObject(options.json, "--json") : {};
|
|
@@ -16294,7 +16974,8 @@ Next page: --cursor ${result.nextCursor}
|
|
|
16294
16974
|
SETTABLE_COMMUNITY_EVENT_STATUSES,
|
|
16295
16975
|
"--status"
|
|
16296
16976
|
)
|
|
16297
|
-
} : {}
|
|
16977
|
+
} : {},
|
|
16978
|
+
...options.rsvp !== void 0 ? { rsvpEnabled: parseOnOff(options.rsvp, "--rsvp") } : {}
|
|
16298
16979
|
};
|
|
16299
16980
|
if (Object.keys(input).length === 0) {
|
|
16300
16981
|
throw new Error("Nothing to update \u2014 pass at least one flag (or a --json patch).");
|
|
@@ -16320,6 +17001,31 @@ Next page: --cursor ${result.nextCursor}
|
|
|
16320
17001
|
});
|
|
16321
17002
|
}
|
|
16322
17003
|
);
|
|
17004
|
+
events.command("attendees").description("Who answered \u201EB\u0119d\u0119\u201D (going) / \u201ENie b\u0119d\u0119\u201D (not going), with e-mails (staff only)").argument("<communityId>", "community id").argument("<eventId>", "event id (from `communities events list`)").action(async (communityId, eventId, _, command) => {
|
|
17005
|
+
const flags = getGlobalFlags(command);
|
|
17006
|
+
const result = await clientFor(flags).listCommunityEventAttendees(communityId, eventId);
|
|
17007
|
+
emit(flags, result, () => {
|
|
17008
|
+
const going = result.attendees.filter(
|
|
17009
|
+
(attendee) => attendee.status === "going"
|
|
17010
|
+
);
|
|
17011
|
+
const notGoing = result.attendees.filter(
|
|
17012
|
+
(attendee) => attendee.status === "not_going"
|
|
17013
|
+
);
|
|
17014
|
+
if (!result.rsvpEnabled) {
|
|
17015
|
+
process.stdout.write("RSVP is off for this event (answers given before are kept).\n");
|
|
17016
|
+
}
|
|
17017
|
+
process.stdout.write(`Going (${result.goingCount}):
|
|
17018
|
+
`);
|
|
17019
|
+
printTable(going.map(attendeeRow), ATTENDEE_COLUMNS);
|
|
17020
|
+
process.stdout.write(`
|
|
17021
|
+
Not going (${result.notGoingCount}):
|
|
17022
|
+
`);
|
|
17023
|
+
printTable(notGoing.map(attendeeRow), ATTENDEE_COLUMNS);
|
|
17024
|
+
if (result.truncated) {
|
|
17025
|
+
process.stdout.write("\nThe list stops at 1000 answers; the counts are exact.\n");
|
|
17026
|
+
}
|
|
17027
|
+
});
|
|
17028
|
+
});
|
|
16323
17029
|
const moderation = communities2.command("moderation").description("Reported posts and comments \u2014 review the queue and resolve reports (staff)");
|
|
16324
17030
|
moderation.command("queue").description("Show pending reports plus the most recently resolved ones").argument("<communityId>", "community id").action(async (communityId, _, command) => {
|
|
16325
17031
|
const flags = getGlobalFlags(command);
|
|
@@ -16373,6 +17079,7 @@ var SETTINGS_PATCH_KEYS = [
|
|
|
16373
17079
|
"publicAccessTermsUrl",
|
|
16374
17080
|
"contentVisibleFromJoinDate",
|
|
16375
17081
|
"eventsEnabled",
|
|
17082
|
+
"eventAnnouncements",
|
|
16376
17083
|
"directMessagesEnabled",
|
|
16377
17084
|
"gamification",
|
|
16378
17085
|
"moderation",
|
|
@@ -17631,7 +18338,7 @@ function registerDiscountsCommands(program2) {
|
|
|
17631
18338
|
page.items.map((d) => ({
|
|
17632
18339
|
id: d.id,
|
|
17633
18340
|
code: d.code,
|
|
17634
|
-
value: formatDiscountValue(d.percentOff, d.amountOffCents, d.currency),
|
|
18341
|
+
value: formatDiscountValue(d.percentOff, d.amountOffCents, d.currency, d.amountScope),
|
|
17635
18342
|
active: d.active ? "yes" : "no",
|
|
17636
18343
|
uses: d.usageCount
|
|
17637
18344
|
})),
|
|
@@ -17644,7 +18351,10 @@ function registerDiscountsCommands(program2) {
|
|
|
17644
18351
|
"limit to a product (repeat the flag for several)",
|
|
17645
18352
|
collectRepeatable4,
|
|
17646
18353
|
[]
|
|
17647
|
-
).option("--max-redemptions <n>", "maximum number of redemptions").option("--ends <isoDate>", "date the code stops working (ISO, e.g. 2026-12-31)").option("--starts <isoDate>", "date the code starts working (ISO, e.g. 2026-12-01)").option("--forever", "apply to every renewal of a subscription (default: first payment only)").option(
|
|
18354
|
+
).option("--max-redemptions <n>", "maximum number of redemptions").option("--ends <isoDate>", "date the code stops working (ISO, e.g. 2026-12-31)").option("--starts <isoDate>", "date the code starts working (ISO, e.g. 2026-12-01)").option("--forever", "apply to every renewal of a subscription (default: first payment only)").option(
|
|
18355
|
+
"--per-unit",
|
|
18356
|
+
"with --amount-off: take the amount off each unit bought (default: once per order)"
|
|
18357
|
+
).option("--inactive", "create the code deactivated (default: active)").action(
|
|
17648
18358
|
async (options, command) => {
|
|
17649
18359
|
const flags = getGlobalFlags(command);
|
|
17650
18360
|
if (options.percentOff === void 0 === (options.amountOff === void 0)) {
|
|
@@ -17656,12 +18366,15 @@ function registerDiscountsCommands(program2) {
|
|
|
17656
18366
|
if (options.currency !== void 0 && options.amountOff === void 0) {
|
|
17657
18367
|
throw new Error("--currency only applies with --amount-off.");
|
|
17658
18368
|
}
|
|
18369
|
+
if (options.perUnit && options.amountOff === void 0) {
|
|
18370
|
+
throw new Error("--per-unit only applies with --amount-off.");
|
|
18371
|
+
}
|
|
17659
18372
|
const percentOff = options.percentOff !== void 0 ? parsePercent(options.percentOff, "--percent-off") : void 0;
|
|
17660
18373
|
const amountOffCents = options.amountOff !== void 0 ? parsePositiveInt8(options.amountOff, "--amount-off") : void 0;
|
|
17661
18374
|
const maxRedemptions = options.maxRedemptions !== void 0 ? parsePositiveInt8(options.maxRedemptions, "--max-redemptions") : void 0;
|
|
17662
18375
|
const endsAt = options.ends !== void 0 ? parseIsoDate(options.ends, "--ends") : void 0;
|
|
17663
18376
|
const startsAt = options.starts !== void 0 ? parseIsoDate(options.starts, "--starts") : void 0;
|
|
17664
|
-
const
|
|
18377
|
+
const duration2 = options.forever ? "forever" : void 0;
|
|
17665
18378
|
const client = createApiClient({ apiKey: flags.apiKey, verbose: flags.verbose });
|
|
17666
18379
|
const result = await client.createDiscount({
|
|
17667
18380
|
code: options.code,
|
|
@@ -17672,14 +18385,16 @@ function registerDiscountsCommands(program2) {
|
|
|
17672
18385
|
maxRedemptions,
|
|
17673
18386
|
endsAt,
|
|
17674
18387
|
startsAt,
|
|
17675
|
-
duration,
|
|
18388
|
+
duration: duration2,
|
|
18389
|
+
amountScope: options.perUnit ? "unit" : void 0,
|
|
17676
18390
|
active: options.inactive ? false : void 0
|
|
17677
18391
|
});
|
|
17678
18392
|
emit(flags, result, () => {
|
|
17679
18393
|
const value = formatDiscountValue(
|
|
17680
18394
|
result.percentOff,
|
|
17681
18395
|
result.amountOffCents,
|
|
17682
|
-
result.currency
|
|
18396
|
+
result.currency,
|
|
18397
|
+
result.amountScope
|
|
17683
18398
|
);
|
|
17684
18399
|
const inactiveNote = result.active ? "" : " [inactive]";
|
|
17685
18400
|
process.stdout.write(
|
|
@@ -17694,7 +18409,10 @@ function registerDiscountsCommands(program2) {
|
|
|
17694
18409
|
"replace the product restriction (repeat the flag for several)",
|
|
17695
18410
|
collectRepeatable4,
|
|
17696
18411
|
[]
|
|
17697
|
-
).option("--max-redemptions <n>", "maximum number of redemptions").option("--clear-max-redemptions", "remove the redemption limit").option("--ends <isoDate>", "date the code stops working (ISO, e.g. 2026-12-31)").option("--clear-ends", "remove the end date").option("--forever", "apply to every renewal of a subscription").option(
|
|
18412
|
+
).option("--max-redemptions <n>", "maximum number of redemptions").option("--clear-max-redemptions", "remove the redemption limit").option("--ends <isoDate>", "date the code stops working (ISO, e.g. 2026-12-31)").option("--clear-ends", "remove the end date").option("--forever", "apply to every renewal of a subscription").option(
|
|
18413
|
+
"--amount-scope <scope>",
|
|
18414
|
+
"fixed amounts: 'order' (once per order) or 'unit' (off each unit bought)"
|
|
18415
|
+
).option("--active", "activate the code").option("--inactive", "deactivate the code").action(
|
|
17698
18416
|
async (discountId, options, command) => {
|
|
17699
18417
|
const flags = getGlobalFlags(command);
|
|
17700
18418
|
if (options.percentOff !== void 0 && options.amountOff !== void 0) {
|
|
@@ -17725,6 +18443,9 @@ function registerDiscountsCommands(program2) {
|
|
|
17725
18443
|
if (options.ends !== void 0) input.endsAt = parseIsoDate(options.ends, "--ends");
|
|
17726
18444
|
if (options.clearEnds) input.endsAt = null;
|
|
17727
18445
|
if (options.forever) input.duration = "forever";
|
|
18446
|
+
if (options.amountScope !== void 0) {
|
|
18447
|
+
input.amountScope = parseAmountScope(options.amountScope);
|
|
18448
|
+
}
|
|
17728
18449
|
if (options.active) input.active = true;
|
|
17729
18450
|
if (options.inactive) input.active = false;
|
|
17730
18451
|
if (Object.keys(input).length === 0) {
|
|
@@ -17738,7 +18459,8 @@ function registerDiscountsCommands(program2) {
|
|
|
17738
18459
|
const value = formatDiscountValue(
|
|
17739
18460
|
result.percentOff,
|
|
17740
18461
|
result.amountOffCents,
|
|
17741
|
-
result.currency
|
|
18462
|
+
result.currency,
|
|
18463
|
+
result.amountScope
|
|
17742
18464
|
);
|
|
17743
18465
|
const state = result.active ? "active" : "inactive";
|
|
17744
18466
|
process.stdout.write(
|
|
@@ -17771,12 +18493,18 @@ function registerDiscountsCommands(program2) {
|
|
|
17771
18493
|
});
|
|
17772
18494
|
});
|
|
17773
18495
|
}
|
|
17774
|
-
function formatDiscountValue(percentOff, amountOffCents, currency) {
|
|
18496
|
+
function formatDiscountValue(percentOff, amountOffCents, currency, amountScope) {
|
|
17775
18497
|
if (percentOff !== void 0) return `${percentOff}%`;
|
|
17776
|
-
if (amountOffCents !== void 0)
|
|
17777
|
-
|
|
18498
|
+
if (amountOffCents !== void 0) {
|
|
18499
|
+
const amount = `${(amountOffCents / 100).toFixed(2)} ${currency ?? ""}`.trim();
|
|
18500
|
+
return amountScope === "unit" ? `${amount} per unit` : amount;
|
|
18501
|
+
}
|
|
17778
18502
|
return "";
|
|
17779
18503
|
}
|
|
18504
|
+
function parseAmountScope(value) {
|
|
18505
|
+
if (value === "order" || value === "unit") return value;
|
|
18506
|
+
throw new Error("--amount-scope must be 'order' or 'unit'.");
|
|
18507
|
+
}
|
|
17780
18508
|
|
|
17781
18509
|
// src/commands/emails/index.ts
|
|
17782
18510
|
import { readFileSync as readFileSync12 } from "fs";
|
|
@@ -18238,20 +18966,357 @@ function registerEmailsCommands(program2) {
|
|
|
18238
18966
|
printBranding(result);
|
|
18239
18967
|
});
|
|
18240
18968
|
});
|
|
18241
|
-
emails.command("test <key>").description(
|
|
18242
|
-
"Queue a test send of one system email (saved customization, sample data) to YOUR own address"
|
|
18243
|
-
).option("--lang <en|pl>", "language to render (defaults to 'pl')").action(async (key, options, command) => {
|
|
18969
|
+
emails.command("test <key>").description(
|
|
18970
|
+
"Queue a test send of one system email (saved customization, sample data) to YOUR own address"
|
|
18971
|
+
).option("--lang <en|pl>", "language to render (defaults to 'pl')").action(async (key, options, command) => {
|
|
18972
|
+
const flags = getGlobalFlags(command);
|
|
18973
|
+
const language = parseLanguage(options.lang, "pl");
|
|
18974
|
+
const client = createApiClient({ apiKey: flags.apiKey, verbose: flags.verbose });
|
|
18975
|
+
const result = await client.sendEmailTemplateTest(key, language);
|
|
18976
|
+
emit(flags, result, () => {
|
|
18977
|
+
process.stdout.write(`Test queued \u2192 ${result.email}
|
|
18978
|
+
`);
|
|
18979
|
+
});
|
|
18980
|
+
});
|
|
18981
|
+
registerEmailLibraryCommands(emails);
|
|
18982
|
+
registerEmailSenderCommands(emails);
|
|
18983
|
+
}
|
|
18984
|
+
|
|
18985
|
+
// src/commands/files/index.ts
|
|
18986
|
+
import { readFile as readFile2, stat as stat2 } from "fs/promises";
|
|
18987
|
+
import { resolve } from "path";
|
|
18988
|
+
|
|
18989
|
+
// src/commands/files/localFileSet.ts
|
|
18990
|
+
import { createHash as createHash3 } from "crypto";
|
|
18991
|
+
import { readdir, readFile, stat } from "fs/promises";
|
|
18992
|
+
import { join as join2, relative, sep } from "path";
|
|
18993
|
+
var MAX_FILE_BYTES = 15 * 1024 * 1024;
|
|
18994
|
+
var MAX_OPERATIONS_PER_REQUEST = 400;
|
|
18995
|
+
var ALWAYS_IGNORED = /* @__PURE__ */ new Set([".git", "node_modules", ".DS_Store", "Thumbs.db"]);
|
|
18996
|
+
var ZANFIA_IGNORE_FILE = ".zanfiaignore";
|
|
18997
|
+
var IGNORE_FILES = [".gitignore", ZANFIA_IGNORE_FILE];
|
|
18998
|
+
var escapeRegex = (text) => text.replace(/[.+^${}()|\\]/g, "\\$&");
|
|
18999
|
+
var globToRegex = (glob) => {
|
|
19000
|
+
let out = "";
|
|
19001
|
+
for (let index = 0; index < glob.length; index += 1) {
|
|
19002
|
+
const char = glob[index];
|
|
19003
|
+
if (char === "*") {
|
|
19004
|
+
if (glob[index + 1] === "*") {
|
|
19005
|
+
const slashAfter = glob[index + 2] === "/";
|
|
19006
|
+
out += slashAfter ? "(?:.*/)?" : ".*";
|
|
19007
|
+
index += slashAfter ? 2 : 1;
|
|
19008
|
+
} else {
|
|
19009
|
+
out += "[^/]*";
|
|
19010
|
+
}
|
|
19011
|
+
} else if (char === "?") {
|
|
19012
|
+
out += "[^/]";
|
|
19013
|
+
} else if (char === "[") {
|
|
19014
|
+
const end = glob.indexOf("]", index + 1);
|
|
19015
|
+
if (end === -1) {
|
|
19016
|
+
out += "\\[";
|
|
19017
|
+
} else {
|
|
19018
|
+
const body = glob.slice(index + 1, end);
|
|
19019
|
+
out += `[${body.startsWith("!") ? `^${body.slice(1)}` : body}]`;
|
|
19020
|
+
index = end;
|
|
19021
|
+
}
|
|
19022
|
+
} else {
|
|
19023
|
+
out += escapeRegex(char ?? "");
|
|
19024
|
+
}
|
|
19025
|
+
}
|
|
19026
|
+
return out;
|
|
19027
|
+
};
|
|
19028
|
+
var parseIgnoreRules = (text) => text.split(/\r?\n/).flatMap((rawLine) => {
|
|
19029
|
+
let line = rawLine.replace(/\s+$/, "");
|
|
19030
|
+
if (!line || line.startsWith("#")) return [];
|
|
19031
|
+
const negated = line.startsWith("!");
|
|
19032
|
+
if (negated) line = line.slice(1);
|
|
19033
|
+
if (line.startsWith("\\")) line = line.slice(1);
|
|
19034
|
+
const dirOnly = line.endsWith("/");
|
|
19035
|
+
if (dirOnly) line = line.slice(0, -1);
|
|
19036
|
+
if (!line) return [];
|
|
19037
|
+
const anchored = line.includes("/");
|
|
19038
|
+
if (line.startsWith("/")) line = line.slice(1);
|
|
19039
|
+
const body = globToRegex(line);
|
|
19040
|
+
return [
|
|
19041
|
+
{
|
|
19042
|
+
regex: new RegExp(anchored ? `^${body}$` : `^(?:.*/)?${body}$`),
|
|
19043
|
+
negated,
|
|
19044
|
+
dirOnly
|
|
19045
|
+
}
|
|
19046
|
+
];
|
|
19047
|
+
});
|
|
19048
|
+
var isIgnoredByRules = (path2, isDir, rules) => {
|
|
19049
|
+
let ignored = false;
|
|
19050
|
+
for (const rule of rules) {
|
|
19051
|
+
if (rule.dirOnly && !isDir) continue;
|
|
19052
|
+
if (rule.regex.test(path2)) ignored = !rule.negated;
|
|
19053
|
+
}
|
|
19054
|
+
return ignored;
|
|
19055
|
+
};
|
|
19056
|
+
var TEMPLATE_SUFFIXES = ["example", "sample", "template", "dist", "defaults"];
|
|
19057
|
+
var CREDENTIALS_FILE_NAMES = [
|
|
19058
|
+
".netrc",
|
|
19059
|
+
"_netrc",
|
|
19060
|
+
".git-credentials",
|
|
19061
|
+
".pypirc",
|
|
19062
|
+
".pgpass",
|
|
19063
|
+
"secrets.json",
|
|
19064
|
+
"secrets.yml",
|
|
19065
|
+
"secrets.yaml"
|
|
19066
|
+
];
|
|
19067
|
+
var secretFileReason = (path2) => {
|
|
19068
|
+
const name = (path2.split("/").pop() ?? path2).toLowerCase();
|
|
19069
|
+
const segments = name.split(".");
|
|
19070
|
+
const first = segments[0] ?? "";
|
|
19071
|
+
const last = segments[segments.length - 1] ?? "";
|
|
19072
|
+
if (segments.length > 1 && (TEMPLATE_SUFFIXES.includes(last) || TEMPLATE_SUFFIXES.includes(first))) {
|
|
19073
|
+
return null;
|
|
19074
|
+
}
|
|
19075
|
+
if (name === ".env" || name === ".envrc" || /^\.env[._-]/.test(name) || name.endsWith(".env")) {
|
|
19076
|
+
return "env-file";
|
|
19077
|
+
}
|
|
19078
|
+
if (["id_rsa", "id_dsa", "id_ecdsa", "id_ed25519"].includes(name)) return "ssh-key";
|
|
19079
|
+
if (CREDENTIALS_FILE_NAMES.includes(name)) return "credentials-file";
|
|
19080
|
+
if (name === "credentials" && path2.toLowerCase().split("/").includes(".aws")) {
|
|
19081
|
+
return "credentials-file";
|
|
19082
|
+
}
|
|
19083
|
+
const extension = segments.length > 1 ? last : "";
|
|
19084
|
+
if (["pem", "key", "p12", "pfx", "jks", "keystore", "ppk", "kdbx"].includes(extension)) {
|
|
19085
|
+
return "key-file";
|
|
19086
|
+
}
|
|
19087
|
+
return null;
|
|
19088
|
+
};
|
|
19089
|
+
var readIgnoreRules = async (root) => {
|
|
19090
|
+
const rules = [];
|
|
19091
|
+
for (const name of IGNORE_FILES) {
|
|
19092
|
+
try {
|
|
19093
|
+
rules.push(...parseIgnoreRules(await readFile(join2(root, name), "utf8")));
|
|
19094
|
+
} catch {
|
|
19095
|
+
}
|
|
19096
|
+
}
|
|
19097
|
+
return rules;
|
|
19098
|
+
};
|
|
19099
|
+
var scanLocalFileSet = async (root) => {
|
|
19100
|
+
const rules = await readIgnoreRules(root);
|
|
19101
|
+
const scan = { files: [], ignored: 0, refused: [] };
|
|
19102
|
+
const walk = async (directory) => {
|
|
19103
|
+
const entries = await readdir(directory, { withFileTypes: true });
|
|
19104
|
+
for (const entry of entries.sort((left, right) => left.name.localeCompare(right.name))) {
|
|
19105
|
+
const absolutePath = join2(directory, entry.name);
|
|
19106
|
+
const path2 = relative(root, absolutePath).split(sep).join("/").normalize("NFC");
|
|
19107
|
+
const isDir = entry.isDirectory();
|
|
19108
|
+
if (ALWAYS_IGNORED.has(entry.name) || // `.zanfiaignore` lists what the creator keeps private: it never ships.
|
|
19109
|
+
path2 === ZANFIA_IGNORE_FILE || isIgnoredByRules(path2, isDir, rules)) {
|
|
19110
|
+
scan.ignored += 1;
|
|
19111
|
+
continue;
|
|
19112
|
+
}
|
|
19113
|
+
if (isDir) {
|
|
19114
|
+
await walk(absolutePath);
|
|
19115
|
+
continue;
|
|
19116
|
+
}
|
|
19117
|
+
if (!entry.isFile()) continue;
|
|
19118
|
+
const secret = secretFileReason(path2);
|
|
19119
|
+
if (secret) {
|
|
19120
|
+
scan.refused.push({ path: path2, reason: `secret (${secret})` });
|
|
19121
|
+
continue;
|
|
19122
|
+
}
|
|
19123
|
+
const { size } = await stat(absolutePath);
|
|
19124
|
+
if (size > MAX_FILE_BYTES) {
|
|
19125
|
+
scan.refused.push({ path: path2, reason: "larger than 15 MB" });
|
|
19126
|
+
continue;
|
|
19127
|
+
}
|
|
19128
|
+
const sha256 = createHash3("sha256").update(await readFile(absolutePath)).digest("hex");
|
|
19129
|
+
scan.files.push({ path: path2, size, sha256, absolutePath });
|
|
19130
|
+
}
|
|
19131
|
+
};
|
|
19132
|
+
await walk(root);
|
|
19133
|
+
return scan;
|
|
19134
|
+
};
|
|
19135
|
+
var planPush = (local, remote, options) => {
|
|
19136
|
+
const prefix = options.targetFolder ? `${options.targetFolder}/` : "";
|
|
19137
|
+
const mapped = local.map((file) => ({ ...file, path: `${prefix}${file.path}` }));
|
|
19138
|
+
const remoteByPath = new Map(remote.map((file) => [file.path, file.sha256]));
|
|
19139
|
+
const localPaths = new Set(mapped.map((file) => file.path));
|
|
19140
|
+
const plan = { added: [], updated: [], unchanged: [], missing: [] };
|
|
19141
|
+
for (const file of mapped) {
|
|
19142
|
+
const remoteSha = remoteByPath.get(file.path);
|
|
19143
|
+
if (remoteSha === void 0) plan.added.push(file);
|
|
19144
|
+
else if (remoteSha === file.sha256) plan.unchanged.push(file.path);
|
|
19145
|
+
else plan.updated.push(file);
|
|
19146
|
+
}
|
|
19147
|
+
plan.missing = remote.map((file) => file.path).filter((path2) => path2.startsWith(prefix) && !localPaths.has(path2));
|
|
19148
|
+
return plan;
|
|
19149
|
+
};
|
|
19150
|
+
var chunkPaths = (paths, size = MAX_OPERATIONS_PER_REQUEST) => {
|
|
19151
|
+
const chunks = [];
|
|
19152
|
+
for (let index = 0; index < paths.length; index += size) {
|
|
19153
|
+
chunks.push(paths.slice(index, index + size));
|
|
19154
|
+
}
|
|
19155
|
+
return chunks;
|
|
19156
|
+
};
|
|
19157
|
+
var PUSH_BATCH_MAX_BYTES = 12 * 1024 * 1024;
|
|
19158
|
+
var PUSH_BATCH_MAX_FILES = 200;
|
|
19159
|
+
var batchForPush = (files) => {
|
|
19160
|
+
const batches = [];
|
|
19161
|
+
let current = [];
|
|
19162
|
+
let bytes = 0;
|
|
19163
|
+
for (const file of files) {
|
|
19164
|
+
if (current.length > 0 && (bytes + file.size > PUSH_BATCH_MAX_BYTES || current.length >= PUSH_BATCH_MAX_FILES)) {
|
|
19165
|
+
batches.push(current);
|
|
19166
|
+
current = [];
|
|
19167
|
+
bytes = 0;
|
|
19168
|
+
}
|
|
19169
|
+
current.push(file);
|
|
19170
|
+
bytes += file.size;
|
|
19171
|
+
}
|
|
19172
|
+
if (current.length > 0) batches.push(current);
|
|
19173
|
+
return batches;
|
|
19174
|
+
};
|
|
19175
|
+
|
|
19176
|
+
// src/commands/files/index.ts
|
|
19177
|
+
var parseTargetFolder = (value) => {
|
|
19178
|
+
if (!value) return "";
|
|
19179
|
+
const folder = value.normalize("NFC").replace(/\\/g, "/").replace(/^\/+|\/+$/g, "");
|
|
19180
|
+
if (folder.split("/").some((segment) => !segment || segment === "." || segment === "..")) {
|
|
19181
|
+
throw new Error(`--to "${value}" is not a folder path inside the set.`);
|
|
19182
|
+
}
|
|
19183
|
+
return folder;
|
|
19184
|
+
};
|
|
19185
|
+
var formatBytes = (bytes) => {
|
|
19186
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
19187
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
19188
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
19189
|
+
};
|
|
19190
|
+
var filesOffNotice = (answer) => answer.fileSetEnabled === false && answer.fileCount > 0 ? "Files are off for buyers (\u201EPliki dla kupuj\u0105cych\u201D is off): turn it on in the product's Settings.\n" : null;
|
|
19191
|
+
var writeFilesOffNotice = (answer) => {
|
|
19192
|
+
const notice = filesOffNotice(answer);
|
|
19193
|
+
if (notice) process.stderr.write(notice);
|
|
19194
|
+
};
|
|
19195
|
+
var requireProduct = (options) => {
|
|
19196
|
+
if (!options.product) throw new Error("Provide the product via --product <id>.");
|
|
19197
|
+
return options.product;
|
|
19198
|
+
};
|
|
19199
|
+
function registerFilesCommands(program2) {
|
|
19200
|
+
const files = program2.command("files").description("A product's \u201EPliki\u201D: the folder tree its buyers browse and download");
|
|
19201
|
+
files.command("list").description("List the files of a product (path, size, sha256)").requiredOption("--product <id>", "product id").action(async (options, command) => {
|
|
19202
|
+
const flags = getGlobalFlags(command);
|
|
19203
|
+
const client = createApiClient({ apiKey: flags.apiKey, verbose: flags.verbose });
|
|
19204
|
+
const fileSet = await client.getProductFileSet(requireProduct(options));
|
|
19205
|
+
emit(flags, fileSet, () => {
|
|
19206
|
+
process.stdout.write(
|
|
19207
|
+
`${fileSet.fileCount} files, ${formatBytes(fileSet.totalBytes)}, revision ${fileSet.revision}${fileSet.lastMessage ? ` (${fileSet.lastMessage})` : ""}
|
|
19208
|
+
`
|
|
19209
|
+
);
|
|
19210
|
+
writeFilesOffNotice(fileSet);
|
|
19211
|
+
printTable(
|
|
19212
|
+
fileSet.files.map((file) => ({
|
|
19213
|
+
path: file.path,
|
|
19214
|
+
size: formatBytes(file.size),
|
|
19215
|
+
sha256: file.sha256.slice(0, 12)
|
|
19216
|
+
})),
|
|
19217
|
+
["path", "size", "sha256"]
|
|
19218
|
+
);
|
|
19219
|
+
});
|
|
19220
|
+
});
|
|
19221
|
+
files.command("push").description(
|
|
19222
|
+
"Push a folder into the product's \u201EPliki\u201D: uploads only changed files (by sha256), honours the folder's root .gitignore / .zanfiaignore, refuses secrets. Files of the set that are not in the folder are KEPT unless you pass --delete"
|
|
19223
|
+
).argument("<dir>", "folder to push").requiredOption("--product <id>", "product id").option("--to <folder>", "folder of the set to push into (default: the set's root)").option("-m, --message <text>", "what changed (kept as the set's latest note)").option("--dry-run", "print the plan, send nothing").option(
|
|
19224
|
+
"--delete",
|
|
19225
|
+
"also delete set files (inside --to) that are not in the folder: a mirror push"
|
|
19226
|
+
).action(async (dir, options, command) => {
|
|
18244
19227
|
const flags = getGlobalFlags(command);
|
|
18245
|
-
const
|
|
18246
|
-
const
|
|
18247
|
-
const
|
|
18248
|
-
|
|
18249
|
-
|
|
19228
|
+
const productId = requireProduct(options);
|
|
19229
|
+
const targetFolder = parseTargetFolder(options.to);
|
|
19230
|
+
const root = resolve(dir);
|
|
19231
|
+
if (!(await stat2(root)).isDirectory()) throw new Error(`${dir} is not a folder.`);
|
|
19232
|
+
const scan = await scanLocalFileSet(root);
|
|
19233
|
+
if (scan.refused.length > 0) {
|
|
19234
|
+
throw new Error(
|
|
19235
|
+
`Nothing pushed. Remove or ignore these files first:
|
|
19236
|
+
${scan.refused.map((item) => ` ${item.path}: ${item.reason}`).join("\n")}`
|
|
19237
|
+
);
|
|
19238
|
+
}
|
|
19239
|
+
const client = createApiClient({ apiKey: flags.apiKey, verbose: flags.verbose });
|
|
19240
|
+
const remote = await client.getProductFileSet(productId);
|
|
19241
|
+
const plan = planPush(scan.files, remote.files, { targetFolder });
|
|
19242
|
+
const uploads = [...plan.added, ...plan.updated];
|
|
19243
|
+
const deletes = options.delete ? plan.missing : [];
|
|
19244
|
+
const summary = {
|
|
19245
|
+
added: plan.added.map((file) => file.path),
|
|
19246
|
+
updated: plan.updated.map((file) => file.path),
|
|
19247
|
+
deleted: deletes,
|
|
19248
|
+
kept: options.delete ? [] : plan.missing,
|
|
19249
|
+
unchanged: plan.unchanged.length,
|
|
19250
|
+
ignored: scan.ignored
|
|
19251
|
+
};
|
|
19252
|
+
const printPlan = () => {
|
|
19253
|
+
for (const path2 of summary.added) process.stdout.write(` + ${path2}
|
|
19254
|
+
`);
|
|
19255
|
+
for (const path2 of summary.updated) process.stdout.write(` ~ ${path2}
|
|
19256
|
+
`);
|
|
19257
|
+
for (const path2 of summary.deleted) process.stdout.write(` - ${path2}
|
|
19258
|
+
`);
|
|
19259
|
+
process.stdout.write(
|
|
19260
|
+
`${summary.added.length} new, ${summary.updated.length} changed, ${summary.deleted.length} deleted, ${summary.unchanged} unchanged, ${summary.ignored} ignored
|
|
19261
|
+
`
|
|
19262
|
+
);
|
|
19263
|
+
if (summary.kept.length > 0) {
|
|
19264
|
+
process.stdout.write(
|
|
19265
|
+
`${summary.kept.length} file(s) of the set are not in this folder and were kept (pass --delete to remove them).
|
|
19266
|
+
`
|
|
19267
|
+
);
|
|
19268
|
+
}
|
|
19269
|
+
};
|
|
19270
|
+
if (options.dryRun || uploads.length === 0 && deletes.length === 0) {
|
|
19271
|
+
emit(flags, { ...summary, dryRun: Boolean(options.dryRun) }, () => {
|
|
19272
|
+
printPlan();
|
|
19273
|
+
if (!options.dryRun) process.stdout.write("Nothing to push: the set is up to date.\n");
|
|
19274
|
+
});
|
|
19275
|
+
return;
|
|
19276
|
+
}
|
|
19277
|
+
const requests = [
|
|
19278
|
+
...batchForPush(uploads).map((batch) => async () => ({
|
|
19279
|
+
files: await Promise.all(
|
|
19280
|
+
batch.map(async (file) => ({
|
|
19281
|
+
path: file.path,
|
|
19282
|
+
contentBase64: (await readFile2(file.absolutePath)).toString("base64")
|
|
19283
|
+
}))
|
|
19284
|
+
)
|
|
19285
|
+
})),
|
|
19286
|
+
...chunkPaths(deletes).map((deletePaths) => async () => ({ deletePaths }))
|
|
19287
|
+
];
|
|
19288
|
+
let result = null;
|
|
19289
|
+
for (const [index, build] of requests.entries()) {
|
|
19290
|
+
const isLast = index === requests.length - 1;
|
|
19291
|
+
const input = await build();
|
|
19292
|
+
try {
|
|
19293
|
+
result = await client.changeProductFileSet(productId, {
|
|
19294
|
+
...input,
|
|
19295
|
+
...isLast && options.message ? { message: options.message } : {}
|
|
19296
|
+
});
|
|
19297
|
+
} catch (error) {
|
|
19298
|
+
if (index > 0) {
|
|
19299
|
+
process.stderr.write(
|
|
19300
|
+
`Stopped at request ${index + 1}/${requests.length}: the ${index} before it were saved, nothing after it was sent. Fix the files below and push again (unchanged files are skipped).
|
|
19301
|
+
`
|
|
19302
|
+
);
|
|
19303
|
+
}
|
|
19304
|
+
throw error;
|
|
19305
|
+
}
|
|
19306
|
+
if (!flags.json && requests.length > 1) {
|
|
19307
|
+
process.stderr.write(` sent ${index + 1}/${requests.length}
|
|
18250
19308
|
`);
|
|
19309
|
+
}
|
|
19310
|
+
}
|
|
19311
|
+
emit(flags, { ...summary, revision: result?.revision ?? remote.revision }, () => {
|
|
19312
|
+
printPlan();
|
|
19313
|
+
process.stdout.write(
|
|
19314
|
+
`Pushed: revision ${result?.revision ?? remote.revision}, ${result?.fileCount ?? 0} files, ${formatBytes(result?.totalBytes ?? 0)}.
|
|
19315
|
+
`
|
|
19316
|
+
);
|
|
19317
|
+
if (result) writeFilesOffNotice(result);
|
|
18251
19318
|
});
|
|
18252
19319
|
});
|
|
18253
|
-
registerEmailLibraryCommands(emails);
|
|
18254
|
-
registerEmailSenderCommands(emails);
|
|
18255
19320
|
}
|
|
18256
19321
|
|
|
18257
19322
|
// src/commands/forms/index.ts
|
|
@@ -18618,7 +19683,13 @@ function registerFormsCommands(program2) {
|
|
|
18618
19683
|
|
|
18619
19684
|
// src/commands/funnels/index.ts
|
|
18620
19685
|
import { readFileSync as readFileSync14 } from "fs";
|
|
18621
|
-
var CREATABLE_RECIPES = [
|
|
19686
|
+
var CREATABLE_RECIPES = [
|
|
19687
|
+
"lead_magnet",
|
|
19688
|
+
"tripwire",
|
|
19689
|
+
"low_ticket",
|
|
19690
|
+
"sell_product",
|
|
19691
|
+
"webinar"
|
|
19692
|
+
];
|
|
18622
19693
|
var FUNNEL_STATUSES = ["draft", "live", "archived"];
|
|
18623
19694
|
var FUNNEL_LANGS = ["en", "pl"];
|
|
18624
19695
|
var FUNNEL_TEMPLATE_IDS = [
|
|
@@ -18657,6 +19728,13 @@ function withLiveGateHint(error, funnelId) {
|
|
|
18657
19728
|
}
|
|
18658
19729
|
throw error;
|
|
18659
19730
|
}
|
|
19731
|
+
function parseMeetingLink(value, flag) {
|
|
19732
|
+
const trimmed = value.trim();
|
|
19733
|
+
if (!/^https?:\/\/\S+$/i.test(trimmed)) {
|
|
19734
|
+
throw new Error(`Invalid ${flag}: ${value} (expected an http(s) link)`);
|
|
19735
|
+
}
|
|
19736
|
+
return trimmed;
|
|
19737
|
+
}
|
|
18660
19738
|
function parsePositiveInt10(value, flag) {
|
|
18661
19739
|
const n = Number.parseInt(value, 10);
|
|
18662
19740
|
if (!Number.isInteger(n) || n <= 0) {
|
|
@@ -18758,6 +19836,33 @@ function printFunnelDetail(funnel) {
|
|
|
18758
19836
|
["#", "stepId", "type", "status", "name", "slug"]
|
|
18759
19837
|
);
|
|
18760
19838
|
}
|
|
19839
|
+
function printFunnelEvent(event) {
|
|
19840
|
+
const start = event.startAt ? `${event.startAt} (${event.timezone}, ${event.durationMinutes} min)` : "on demand";
|
|
19841
|
+
process.stdout.write(` Webinar (page ${event.pageId}):
|
|
19842
|
+
`);
|
|
19843
|
+
process.stdout.write(` Start: ${start}
|
|
19844
|
+
`);
|
|
19845
|
+
process.stdout.write(` Join link: ${event.joinUrl ?? "(none)"}
|
|
19846
|
+
`);
|
|
19847
|
+
if (event.replayUrl) process.stdout.write(` Recording: ${event.replayUrl}
|
|
19848
|
+
`);
|
|
19849
|
+
if (event.liveRoomId) process.stdout.write(` Runs on webinar: ${event.liveRoomId}
|
|
19850
|
+
`);
|
|
19851
|
+
if (event.rungs.length === 0) {
|
|
19852
|
+
process.stdout.write(" Reminders: none\n");
|
|
19853
|
+
return;
|
|
19854
|
+
}
|
|
19855
|
+
process.stdout.write(" Reminders:\n");
|
|
19856
|
+
printTable(
|
|
19857
|
+
event.rungs.map((rung) => ({
|
|
19858
|
+
kind: rung.kind,
|
|
19859
|
+
sendAt: rung.sendAt,
|
|
19860
|
+
status: rung.status ?? "missing",
|
|
19861
|
+
broadcastId: rung.broadcastId
|
|
19862
|
+
})),
|
|
19863
|
+
["kind", "sendAt", "status", "broadcastId"]
|
|
19864
|
+
);
|
|
19865
|
+
}
|
|
18761
19866
|
async function resolveStepId(client, funnelId, step) {
|
|
18762
19867
|
if (!/^\d+$/.test(step)) return step;
|
|
18763
19868
|
const position = Number.parseInt(step, 10);
|
|
@@ -18783,7 +19888,7 @@ function buildFunnelUpdateInput(options) {
|
|
|
18783
19888
|
}
|
|
18784
19889
|
function registerFunnelsCommands(program2) {
|
|
18785
19890
|
const funnels = program2.command("funnels").description(
|
|
18786
|
-
|
|
19891
|
+
`Manage marketing funnels \u2014 named step chains that REFERENCE existing checkouts, pages, and workflows (a funnel never owns the artifacts its steps point to). Create is recipe-first (${CREATABLE_RECIPES.join(", ")}) or from a template with its pages generated in one go (\`funnels from-template\`).`
|
|
18787
19892
|
);
|
|
18788
19893
|
funnels.command("list").description("List the workspace's funnels").action(async (_, command) => {
|
|
18789
19894
|
const flags = getGlobalFlags(command);
|
|
@@ -18805,10 +19910,13 @@ function registerFunnelsCommands(program2) {
|
|
|
18805
19910
|
const flags = getGlobalFlags(command);
|
|
18806
19911
|
const client = createApiClient({ apiKey: flags.apiKey, verbose: flags.verbose });
|
|
18807
19912
|
const result = await client.getFunnel(id);
|
|
18808
|
-
emit(flags, result, () =>
|
|
19913
|
+
emit(flags, result, () => {
|
|
19914
|
+
printFunnelDetail(result.funnel);
|
|
19915
|
+
if (result.event) printFunnelEvent(result.event);
|
|
19916
|
+
});
|
|
18809
19917
|
});
|
|
18810
19918
|
funnels.command("create").description(
|
|
18811
|
-
"Create a funnel from a recipe over the product's EXISTING artifacts. 'lead_magnet' (free checkout as opt-in \u2192 thank-you) and 'tripwire' (free opt-in \u2192 the checkout's one-time-offer chain \u2192 thank-you) REQUIRE a free checkout on the product; 'sell_product' (sales page \u2192 default paid checkout \u2192 OTO \u2192 thank-you)
|
|
19919
|
+
"Create a funnel from a recipe over the product's EXISTING artifacts. 'lead_magnet' (free checkout as opt-in \u2192 thank-you) and 'tripwire' (free opt-in \u2192 the checkout's one-time-offer chain \u2192 thank-you) REQUIRE a free checkout on the product; 'sell_product' (sales page \u2192 default paid checkout \u2192 OTO \u2192 thank-you) and 'low_ticket' (a cheap product page that takes the payment \u2192 one-time offers \u2192 thank-you) require a paid one. Creates the funnel DRAFT referencing those existing checkouts (no new pages or offers) plus the recipe email sequence as a DRAFT workflow \u2014 nothing sends until the creator publishes it. 'webinar' takes any product and creates three EMPTY page steps (registration \u2192 thank-you \u2192 watch page); generate the registration step next with the event (`funnels generate-page <id> --step 1 --event-start \u2026`), or build it all in one go with `funnels from-template webinar`."
|
|
18812
19920
|
).requiredOption("--recipe <recipe>", `creation recipe (${CREATABLE_RECIPES.join(" | ")})`).requiredOption("--product <productId>", "the product the funnel sells or delivers").option("--name <name>", "custom name; omit for a recipe default").requiredOption("--lang <en|pl>", "language of the generated email copy \u2014 the creator's").action(async (options, command) => {
|
|
18813
19921
|
const flags = getGlobalFlags(command);
|
|
18814
19922
|
const client = createApiClient({ apiKey: flags.apiKey, verbose: flags.verbose });
|
|
@@ -18822,6 +19930,14 @@ function registerFunnelsCommands(program2) {
|
|
|
18822
19930
|
emit(flags, result, () => {
|
|
18823
19931
|
process.stdout.write(`Created funnel ${result.funnelId} (draft).
|
|
18824
19932
|
`);
|
|
19933
|
+
if (input.recipe === "webinar") {
|
|
19934
|
+
process.stdout.write(
|
|
19935
|
+
`Next: generate the registration page with the event (it builds the thank-you and watch pages and the reminder drafts too):
|
|
19936
|
+
funnels generate-page ${result.funnelId} --step 1 --lang ${input.lang} --event-start <ISO 8601 with offset> --event-timezone <IANA zone>
|
|
19937
|
+
(on-demand: --event-mode on_demand instead of the date)
|
|
19938
|
+
`
|
|
19939
|
+
);
|
|
19940
|
+
}
|
|
18825
19941
|
});
|
|
18826
19942
|
});
|
|
18827
19943
|
funnels.command("update").description(
|
|
@@ -18868,11 +19984,23 @@ function registerFunnelsCommands(program2) {
|
|
|
18868
19984
|
});
|
|
18869
19985
|
});
|
|
18870
19986
|
funnels.command("generate-page").description(
|
|
18871
|
-
"AI-generate a hosted DRAFT page for ONE page-type step (
|
|
19987
|
+
"AI-generate a hosted DRAFT page for ONE page-type step (lead-magnet / sales-page / thank-you / webinar-registration \u2014 checkout/oto/downsell steps have their own surfaces) and wire the step to it. A webinar-registration step builds the whole family (thank-you, watch page, reminder drafts) around the event: pass --event-start (+ --event-timezone, --event-duration) or --event-mode on_demand. Consumes AI credits and runs in the background (1-5 minutes) \u2014 call it once per step, never speculatively. Pass the same --template across a funnel\u2019s steps so the pages look like siblings."
|
|
18872
19988
|
).argument("<id>", "funnel id").requiredOption(
|
|
18873
19989
|
"--step <n|id>",
|
|
18874
19990
|
"the page-type step to build \u2014 either its 1-based position (from `funnels get`'s step table) or its raw stepId"
|
|
18875
|
-
).requiredOption("--lang <en|pl>", "language of the generated copy \u2014 the creator's").option("--brief <text>", "optional creator notes appended to the synthesized brief").option("--template <templateId>", "optional design-template seed; reuse across steps").
|
|
19991
|
+
).requiredOption("--lang <en|pl>", "language of the generated copy \u2014 the creator's").option("--brief <text>", "optional creator notes appended to the synthesized brief").option("--template <templateId>", "optional design-template seed; reuse across steps").option(
|
|
19992
|
+
"--event-start <iso>",
|
|
19993
|
+
"webinar registration step: event start as ISO 8601 WITH offset (live)"
|
|
19994
|
+
).option("--event-timezone <tz>", "webinar registration step: IANA timezone, e.g. Europe/Warsaw").option(
|
|
19995
|
+
"--event-duration <minutes>",
|
|
19996
|
+
"webinar registration step: event length in minutes (default 60)"
|
|
19997
|
+
).option(
|
|
19998
|
+
"--event-mode <mode>",
|
|
19999
|
+
`webinar registration step: ${EVENT_MODES.join(" | ")} (on_demand = no date)`
|
|
20000
|
+
).option(
|
|
20001
|
+
"--join-url <url>",
|
|
20002
|
+
"webinar registration step: the meeting link (ClickMeeting, Zoom, Teams, Meet)"
|
|
20003
|
+
).action(
|
|
18876
20004
|
async (id, options, command) => {
|
|
18877
20005
|
const flags = getGlobalFlags(command);
|
|
18878
20006
|
const client = createApiClient({ apiKey: flags.apiKey, verbose: flags.verbose });
|
|
@@ -18881,7 +20009,12 @@ function registerFunnelsCommands(program2) {
|
|
|
18881
20009
|
stepId,
|
|
18882
20010
|
lang: parseChoice3(options.lang, FUNNEL_LANGS, "--lang"),
|
|
18883
20011
|
brief: options.brief,
|
|
18884
|
-
templateId: options.template
|
|
20012
|
+
templateId: options.template,
|
|
20013
|
+
...options.eventStart !== void 0 ? { eventStartAt: options.eventStart } : {},
|
|
20014
|
+
...options.eventTimezone !== void 0 ? { eventTimezone: options.eventTimezone } : {},
|
|
20015
|
+
...options.eventDuration !== void 0 ? { eventDurationMinutes: parsePositiveInt10(options.eventDuration, "--event-duration") } : {},
|
|
20016
|
+
...options.eventMode !== void 0 ? { eventMode: parseChoice3(options.eventMode, EVENT_MODES, "--event-mode") } : {},
|
|
20017
|
+
...options.joinUrl !== void 0 ? { eventJoinUrl: parseMeetingLink(options.joinUrl, "--join-url") } : {}
|
|
18885
20018
|
};
|
|
18886
20019
|
const result = await client.generateFunnelStepPage(id, input);
|
|
18887
20020
|
emit(flags, result, () => {
|
|
@@ -18913,22 +20046,48 @@ function registerFunnelsCommands(program2) {
|
|
|
18913
20046
|
});
|
|
18914
20047
|
});
|
|
18915
20048
|
funnels.command("from-template").description(
|
|
18916
|
-
"Create a funnel from a catalog template (`funnels templates`) AND AI-generate its page steps in one go \u2014 opt-in / sales / thank-you pages are queued as background generations (202; consumes AI credits, 1-5 minutes each). The template\u2019s product requirement must be met (free or paid checkout on the product), else 409. Webinar template: pass the event fields (live) or --event-mode on_demand. Requires --confirm."
|
|
18917
|
-
).argument("<templateId>", `template id (${FUNNEL_TEMPLATE_IDS.join(" | ")})`).
|
|
20049
|
+
"Create a funnel from a catalog template (`funnels templates`) AND AI-generate its page steps in one go \u2014 opt-in / sales / thank-you pages are queued as background generations (202; consumes AI credits, 1-5 minutes each). The template\u2019s product requirement must be met (free or paid checkout on the product), else 409. Webinar template: pass the event fields (live; --join-url for a ClickMeeting / Zoom link) or --event-mode on_demand, or --live-room <roomId> to run it on a webinar that plays a recording (then --product is optional; needs the live_rooms plan feature). Requires --confirm."
|
|
20050
|
+
).argument("<templateId>", `template id (${FUNNEL_TEMPLATE_IDS.join(" | ")})`).option(
|
|
20051
|
+
"--product <productId>",
|
|
20052
|
+
"the product the funnel sells or delivers (required, except with --live-room)"
|
|
20053
|
+
).requiredOption("--lang <en|pl>", "language of the generated pages and emails \u2014 the creator's").option("--name <name>", "custom funnel name; omit for the recipe default").option("--event-start <iso>", "webinar: event start as ISO 8601 WITH offset (live)").option("--event-timezone <tz>", "webinar: IANA timezone, e.g. Europe/Warsaw").option("--event-duration <minutes>", "webinar: event length in minutes (default 60)").option(
|
|
20054
|
+
"--event-mode <mode>",
|
|
20055
|
+
`webinar: ${EVENT_MODES.join(" | ")} (on_demand = on-demand webinar, no date)`
|
|
20056
|
+
).option(
|
|
20057
|
+
"--join-url <url>",
|
|
20058
|
+
"webinar (live): the meeting link (ClickMeeting, Zoom, Teams, Meet) the pages and reminders carry"
|
|
20059
|
+
).option(
|
|
20060
|
+
"--live-room <roomId>",
|
|
20061
|
+
"webinar: a webinar (`live-rooms list`) that plays a recording; its sessions are the event"
|
|
20062
|
+
).option(
|
|
18918
20063
|
"--page-template <id>",
|
|
18919
20064
|
"design template for every generated page (a page-template id, e.g. course-fresh); thank-you steps take the companion"
|
|
18920
20065
|
).option("--confirm", "required \u2014 acknowledge that AI credits will be consumed").action(
|
|
18921
20066
|
async (templateId, options, command) => {
|
|
18922
20067
|
const flags = getGlobalFlags(command);
|
|
20068
|
+
const template = parseChoice3(templateId, FUNNEL_TEMPLATE_IDS, "<templateId>");
|
|
20069
|
+
if (options.liveRoom !== void 0 && template !== "webinar") {
|
|
20070
|
+
throw new Error("--live-room is for the webinar template only.");
|
|
20071
|
+
}
|
|
20072
|
+
if (options.joinUrl !== void 0 && template !== "webinar") {
|
|
20073
|
+
throw new Error("--join-url is for the webinar template only.");
|
|
20074
|
+
}
|
|
20075
|
+
if (options.product === void 0 && options.liveRoom === void 0) {
|
|
20076
|
+
throw new Error(
|
|
20077
|
+
"Missing --product <productId> (only a webinar that plays a recording, --live-room, goes without one)."
|
|
20078
|
+
);
|
|
20079
|
+
}
|
|
18923
20080
|
requireConfirm6(options.confirm, "create the funnel and generate its pages (AI credits)");
|
|
18924
20081
|
const input = {
|
|
18925
|
-
templateId:
|
|
18926
|
-
productId: options.product,
|
|
20082
|
+
templateId: template,
|
|
20083
|
+
...options.product !== void 0 ? { productId: options.product } : {},
|
|
18927
20084
|
lang: parseChoice3(options.lang, FUNNEL_LANGS, "--lang"),
|
|
18928
20085
|
name: options.name,
|
|
18929
20086
|
...options.pageTemplate ? { pageTemplateId: options.pageTemplate } : {}
|
|
18930
20087
|
};
|
|
18931
20088
|
const webinar = {
|
|
20089
|
+
liveRoomId: options.liveRoom,
|
|
20090
|
+
joinUrl: options.joinUrl !== void 0 ? parseMeetingLink(options.joinUrl, "--join-url") : void 0,
|
|
18932
20091
|
eventStartAt: options.eventStart,
|
|
18933
20092
|
eventTimezone: options.eventTimezone,
|
|
18934
20093
|
eventDurationMinutes: options.eventDuration !== void 0 ? parsePositiveInt10(options.eventDuration, "--event-duration") : void 0,
|
|
@@ -19971,6 +21130,16 @@ function registerIntegrationsCommands(program2) {
|
|
|
19971
21130
|
}
|
|
19972
21131
|
|
|
19973
21132
|
// src/commands/join/index.ts
|
|
21133
|
+
function joinApprovalRefusal(err) {
|
|
21134
|
+
if (!(err instanceof HttpJsonError)) return null;
|
|
21135
|
+
if (err.code === "community-join-approval-required" /* ApprovalRequired */) {
|
|
21136
|
+
return err.serverMessage.endsWith(".") ? err.serverMessage : `${err.serverMessage}.`;
|
|
21137
|
+
}
|
|
21138
|
+
if (err.code === "community-join-approval-unavailable" /* ApprovalUnavailable */) {
|
|
21139
|
+
return "We couldn't check your sign-up right now, please try again in a moment.";
|
|
21140
|
+
}
|
|
21141
|
+
return null;
|
|
21142
|
+
}
|
|
19974
21143
|
var MAX_CODE_ATTEMPTS2 = 5;
|
|
19975
21144
|
var HANDLE_REGEX = /^[a-z0-9][a-z0-9-]{1,62}$/;
|
|
19976
21145
|
var EMAIL_REGEX2 = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
@@ -20038,6 +21207,8 @@ async function runJoin(handleInput, options) {
|
|
|
20038
21207
|
{ verbose }
|
|
20039
21208
|
);
|
|
20040
21209
|
} catch (err) {
|
|
21210
|
+
const refusal = joinApprovalRefusal(err);
|
|
21211
|
+
if (refusal) throw new Error(refusal, { cause: err });
|
|
20041
21212
|
if (options.code || attempt === MAX_CODE_ATTEMPTS2) throw err;
|
|
20042
21213
|
process.stdout.write(`${err instanceof Error ? err.message : String(err)}
|
|
20043
21214
|
`);
|
|
@@ -20150,9 +21321,9 @@ function describeSchedule(room) {
|
|
|
20150
21321
|
}
|
|
20151
21322
|
function registerLiveRoomsCommands(program2) {
|
|
20152
21323
|
const liveRooms = program2.command("live-rooms").description(
|
|
20153
|
-
"
|
|
21324
|
+
"Webinars (incl. rolling autowebinar sessions) \u2014 list and attendance; webinars are managed in the dashboard"
|
|
20154
21325
|
);
|
|
20155
|
-
liveRooms.command("list").description("List
|
|
21326
|
+
liveRooms.command("list").description("List webinars (latest start first)").action(async (_, command) => {
|
|
20156
21327
|
const flags = getGlobalFlags(command);
|
|
20157
21328
|
const client = createApiClient({ apiKey: flags.apiKey, verbose: flags.verbose });
|
|
20158
21329
|
const result = await client.listLiveRooms();
|
|
@@ -20171,7 +21342,7 @@ function registerLiveRoomsCommands(program2) {
|
|
|
20171
21342
|
);
|
|
20172
21343
|
});
|
|
20173
21344
|
});
|
|
20174
|
-
liveRooms.command("attendance").description("A
|
|
21345
|
+
liveRooms.command("attendance").description("A webinar's attendance per session; --session adds that session's viewers").argument("<roomId>", "webinar id (from live-rooms list)").option("--session <iso>", "ISO start of one session to list its attendees").action(async (roomId, options, command) => {
|
|
20175
21346
|
const flags = getGlobalFlags(command);
|
|
20176
21347
|
const client = createApiClient({ apiKey: flags.apiKey, verbose: flags.verbose });
|
|
20177
21348
|
const result = await client.getLiveRoomAttendance(roomId, options.session);
|
|
@@ -20207,7 +21378,7 @@ function registerLiveRoomsCommands(program2) {
|
|
|
20207
21378
|
if (totals) {
|
|
20208
21379
|
process.stdout.write(
|
|
20209
21380
|
`
|
|
20210
|
-
|
|
21381
|
+
Webinar total (${totals.sessions} started sessions): registered ${countOrDash(totals.registered)} \u2192 attended ${totals.attendees} \u2192 saw the offer ${totals.sawOffer} \u2192 clicked ${totals.clickedOffer} \u2192 bought ${countOrDash(totals.purchased)}${totals.capped ? " (at least: a read hit its cap)" : ""}
|
|
20211
21382
|
`
|
|
20212
21383
|
);
|
|
20213
21384
|
}
|
|
@@ -20234,14 +21405,14 @@ Viewers of ${options.session}:
|
|
|
20234
21405
|
|
|
20235
21406
|
// src/commands/media/index.ts
|
|
20236
21407
|
import { existsSync, readFileSync as readFileSync17, writeFileSync as writeFileSync4 } from "fs";
|
|
20237
|
-
import { readFile, stat as
|
|
21408
|
+
import { readFile as readFile3, stat as stat5 } from "fs/promises";
|
|
20238
21409
|
import { basename } from "path";
|
|
20239
21410
|
|
|
20240
21411
|
// src/client/bunnyUpload.ts
|
|
20241
21412
|
import { createReadStream as createReadStream2 } from "fs";
|
|
20242
|
-
import { stat as
|
|
21413
|
+
import { stat as stat4 } from "fs/promises";
|
|
20243
21414
|
|
|
20244
|
-
//
|
|
21415
|
+
// ../../node_modules/tus-js-client/lib.esm/error.js
|
|
20245
21416
|
function _typeof(o) {
|
|
20246
21417
|
"@babel/helpers - typeof";
|
|
20247
21418
|
return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(o2) {
|
|
@@ -20395,14 +21566,14 @@ var DetailedError = /* @__PURE__ */ (function(_Error) {
|
|
|
20395
21566
|
})(/* @__PURE__ */ _wrapNativeSuper(Error));
|
|
20396
21567
|
var error_default = DetailedError;
|
|
20397
21568
|
|
|
20398
|
-
//
|
|
21569
|
+
// ../../node_modules/tus-js-client/lib.esm/logger.js
|
|
20399
21570
|
var isEnabled = false;
|
|
20400
21571
|
function log(msg) {
|
|
20401
21572
|
if (!isEnabled) return;
|
|
20402
21573
|
console.log(msg);
|
|
20403
21574
|
}
|
|
20404
21575
|
|
|
20405
|
-
//
|
|
21576
|
+
// ../../node_modules/tus-js-client/lib.esm/noopUrlStorage.js
|
|
20406
21577
|
function _typeof2(o) {
|
|
20407
21578
|
"@babel/helpers - typeof";
|
|
20408
21579
|
return _typeof2 = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(o2) {
|
|
@@ -20472,7 +21643,7 @@ var NoopUrlStorage = /* @__PURE__ */ (function() {
|
|
|
20472
21643
|
}]);
|
|
20473
21644
|
})();
|
|
20474
21645
|
|
|
20475
|
-
//
|
|
21646
|
+
// ../../node_modules/js-base64/base64.mjs
|
|
20476
21647
|
var version = "3.7.8";
|
|
20477
21648
|
var VERSION = version;
|
|
20478
21649
|
var _hasBuffer = typeof Buffer === "function";
|
|
@@ -20634,10 +21805,10 @@ var gBase64 = {
|
|
|
20634
21805
|
extendBuiltins
|
|
20635
21806
|
};
|
|
20636
21807
|
|
|
20637
|
-
//
|
|
21808
|
+
// ../../node_modules/tus-js-client/lib.esm/upload.js
|
|
20638
21809
|
var import_url_parse = __toESM(require_url_parse());
|
|
20639
21810
|
|
|
20640
|
-
//
|
|
21811
|
+
// ../../node_modules/tus-js-client/lib.esm/uuid.js
|
|
20641
21812
|
function uuid() {
|
|
20642
21813
|
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) {
|
|
20643
21814
|
var r = Math.random() * 16 | 0;
|
|
@@ -20646,7 +21817,7 @@ function uuid() {
|
|
|
20646
21817
|
});
|
|
20647
21818
|
}
|
|
20648
21819
|
|
|
20649
|
-
//
|
|
21820
|
+
// ../../node_modules/tus-js-client/lib.esm/upload.js
|
|
20650
21821
|
function _regeneratorRuntime() {
|
|
20651
21822
|
"use strict";
|
|
20652
21823
|
_regeneratorRuntime = function _regeneratorRuntime4() {
|
|
@@ -20885,7 +22056,7 @@ function _regeneratorRuntime() {
|
|
|
20885
22056
|
return this.delegate = { iterator: values(e2), resultName: r2, nextLoc: n2 }, "next" === this.method && (this.arg = t), y;
|
|
20886
22057
|
} }, e;
|
|
20887
22058
|
}
|
|
20888
|
-
function asyncGeneratorStep(gen,
|
|
22059
|
+
function asyncGeneratorStep(gen, resolve3, reject, _next, _throw, key, arg) {
|
|
20889
22060
|
try {
|
|
20890
22061
|
var info = gen[key](arg);
|
|
20891
22062
|
var value = info.value;
|
|
@@ -20894,7 +22065,7 @@ function asyncGeneratorStep(gen, resolve2, reject, _next, _throw, key, arg) {
|
|
|
20894
22065
|
return;
|
|
20895
22066
|
}
|
|
20896
22067
|
if (info.done) {
|
|
20897
|
-
|
|
22068
|
+
resolve3(value);
|
|
20898
22069
|
} else {
|
|
20899
22070
|
Promise.resolve(value).then(_next, _throw);
|
|
20900
22071
|
}
|
|
@@ -20902,13 +22073,13 @@ function asyncGeneratorStep(gen, resolve2, reject, _next, _throw, key, arg) {
|
|
|
20902
22073
|
function _asyncToGenerator(fn) {
|
|
20903
22074
|
return function() {
|
|
20904
22075
|
var self2 = this, args = arguments;
|
|
20905
|
-
return new Promise(function(
|
|
22076
|
+
return new Promise(function(resolve3, reject) {
|
|
20906
22077
|
var gen = fn.apply(self2, args);
|
|
20907
22078
|
function _next(value) {
|
|
20908
|
-
asyncGeneratorStep(gen,
|
|
22079
|
+
asyncGeneratorStep(gen, resolve3, reject, _next, _throw, "next", value);
|
|
20909
22080
|
}
|
|
20910
22081
|
function _throw(err) {
|
|
20911
|
-
asyncGeneratorStep(gen,
|
|
22082
|
+
asyncGeneratorStep(gen, resolve3, reject, _next, _throw, "throw", err);
|
|
20912
22083
|
}
|
|
20913
22084
|
_next(void 0);
|
|
20914
22085
|
});
|
|
@@ -21240,7 +22411,7 @@ var BaseUpload = /* @__PURE__ */ (function() {
|
|
|
21240
22411
|
var lastPartProgress = 0;
|
|
21241
22412
|
return _this3._source.slice(part.start, part.end).then(function(_ref) {
|
|
21242
22413
|
var value = _ref.value;
|
|
21243
|
-
return new Promise(function(
|
|
22414
|
+
return new Promise(function(resolve3, reject) {
|
|
21244
22415
|
var options = _objectSpread(_objectSpread({}, _this3.options), {}, {
|
|
21245
22416
|
// If available, the partial upload should be resumed from a previous URL.
|
|
21246
22417
|
uploadUrl: part.uploadUrl || null,
|
|
@@ -21258,7 +22429,7 @@ var BaseUpload = /* @__PURE__ */ (function() {
|
|
|
21258
22429
|
"Upload-Concat": "partial"
|
|
21259
22430
|
}),
|
|
21260
22431
|
// Reject or resolve the promise if the upload errors or completes.
|
|
21261
|
-
onSuccess:
|
|
22432
|
+
onSuccess: resolve3,
|
|
21262
22433
|
onError: reject,
|
|
21263
22434
|
// Based in the progress for this partial upload, calculate the progress
|
|
21264
22435
|
// for the entire final upload.
|
|
@@ -21769,8 +22940,8 @@ var BaseUpload = /* @__PURE__ */ (function() {
|
|
|
21769
22940
|
var newOptions = _objectSpread(_objectSpread({}, options), {}, {
|
|
21770
22941
|
retryDelays: remainingDelays
|
|
21771
22942
|
});
|
|
21772
|
-
return new Promise(function(
|
|
21773
|
-
return setTimeout(
|
|
22943
|
+
return new Promise(function(resolve3) {
|
|
22944
|
+
return setTimeout(resolve3, delay);
|
|
21774
22945
|
}).then(function() {
|
|
21775
22946
|
return BaseUpload2.terminate(url, newOptions);
|
|
21776
22947
|
});
|
|
@@ -21881,11 +23052,11 @@ function splitSizeIntoParts(totalSize, partCount) {
|
|
|
21881
23052
|
BaseUpload.defaultOptions = defaultOptions;
|
|
21882
23053
|
var upload_default = BaseUpload;
|
|
21883
23054
|
|
|
21884
|
-
//
|
|
23055
|
+
// ../../node_modules/tus-js-client/lib.esm/node/fileReader.js
|
|
21885
23056
|
var import_is_stream = __toESM(require_is_stream());
|
|
21886
23057
|
import { ReadStream } from "fs";
|
|
21887
23058
|
|
|
21888
|
-
//
|
|
23059
|
+
// ../../node_modules/tus-js-client/lib.esm/node/sources/BufferSource.js
|
|
21889
23060
|
function _typeof4(o) {
|
|
21890
23061
|
"@babel/helpers - typeof";
|
|
21891
23062
|
return _typeof4 = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(o2) {
|
|
@@ -21952,7 +23123,7 @@ var BufferSource = /* @__PURE__ */ (function() {
|
|
|
21952
23123
|
}]);
|
|
21953
23124
|
})();
|
|
21954
23125
|
|
|
21955
|
-
//
|
|
23126
|
+
// ../../node_modules/tus-js-client/lib.esm/node/sources/FileSource.js
|
|
21956
23127
|
import { createReadStream, promises as fsPromises } from "fs";
|
|
21957
23128
|
function _typeof5(o) {
|
|
21958
23129
|
"@babel/helpers - typeof";
|
|
@@ -22234,7 +23405,7 @@ function _toPrimitive5(t, r) {
|
|
|
22234
23405
|
}
|
|
22235
23406
|
return ("string" === r ? String : Number)(t);
|
|
22236
23407
|
}
|
|
22237
|
-
function asyncGeneratorStep2(gen,
|
|
23408
|
+
function asyncGeneratorStep2(gen, resolve3, reject, _next, _throw, key, arg) {
|
|
22238
23409
|
try {
|
|
22239
23410
|
var info = gen[key](arg);
|
|
22240
23411
|
var value = info.value;
|
|
@@ -22243,7 +23414,7 @@ function asyncGeneratorStep2(gen, resolve2, reject, _next, _throw, key, arg) {
|
|
|
22243
23414
|
return;
|
|
22244
23415
|
}
|
|
22245
23416
|
if (info.done) {
|
|
22246
|
-
|
|
23417
|
+
resolve3(value);
|
|
22247
23418
|
} else {
|
|
22248
23419
|
Promise.resolve(value).then(_next, _throw);
|
|
22249
23420
|
}
|
|
@@ -22251,13 +23422,13 @@ function asyncGeneratorStep2(gen, resolve2, reject, _next, _throw, key, arg) {
|
|
|
22251
23422
|
function _asyncToGenerator2(fn) {
|
|
22252
23423
|
return function() {
|
|
22253
23424
|
var self2 = this, args = arguments;
|
|
22254
|
-
return new Promise(function(
|
|
23425
|
+
return new Promise(function(resolve3, reject) {
|
|
22255
23426
|
var gen = fn.apply(self2, args);
|
|
22256
23427
|
function _next(value) {
|
|
22257
|
-
asyncGeneratorStep2(gen,
|
|
23428
|
+
asyncGeneratorStep2(gen, resolve3, reject, _next, _throw, "next", value);
|
|
22258
23429
|
}
|
|
22259
23430
|
function _throw(err) {
|
|
22260
|
-
asyncGeneratorStep2(gen,
|
|
23431
|
+
asyncGeneratorStep2(gen, resolve3, reject, _next, _throw, "throw", err);
|
|
22261
23432
|
}
|
|
22262
23433
|
_next(void 0);
|
|
22263
23434
|
});
|
|
@@ -22328,7 +23499,7 @@ var FileSource = /* @__PURE__ */ (function() {
|
|
|
22328
23499
|
}]);
|
|
22329
23500
|
})();
|
|
22330
23501
|
|
|
22331
|
-
//
|
|
23502
|
+
// ../../node_modules/tus-js-client/lib.esm/node/sources/StreamSource.js
|
|
22332
23503
|
function _typeof6(o) {
|
|
22333
23504
|
"@babel/helpers - typeof";
|
|
22334
23505
|
return _typeof6 = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(o2) {
|
|
@@ -22609,7 +23780,7 @@ function _toPrimitive6(t, r) {
|
|
|
22609
23780
|
}
|
|
22610
23781
|
return ("string" === r ? String : Number)(t);
|
|
22611
23782
|
}
|
|
22612
|
-
function asyncGeneratorStep3(gen,
|
|
23783
|
+
function asyncGeneratorStep3(gen, resolve3, reject, _next, _throw, key, arg) {
|
|
22613
23784
|
try {
|
|
22614
23785
|
var info = gen[key](arg);
|
|
22615
23786
|
var value = info.value;
|
|
@@ -22618,7 +23789,7 @@ function asyncGeneratorStep3(gen, resolve2, reject, _next, _throw, key, arg) {
|
|
|
22618
23789
|
return;
|
|
22619
23790
|
}
|
|
22620
23791
|
if (info.done) {
|
|
22621
|
-
|
|
23792
|
+
resolve3(value);
|
|
22622
23793
|
} else {
|
|
22623
23794
|
Promise.resolve(value).then(_next, _throw);
|
|
22624
23795
|
}
|
|
@@ -22626,13 +23797,13 @@ function asyncGeneratorStep3(gen, resolve2, reject, _next, _throw, key, arg) {
|
|
|
22626
23797
|
function _asyncToGenerator3(fn) {
|
|
22627
23798
|
return function() {
|
|
22628
23799
|
var self2 = this, args = arguments;
|
|
22629
|
-
return new Promise(function(
|
|
23800
|
+
return new Promise(function(resolve3, reject) {
|
|
22630
23801
|
var gen = fn.apply(self2, args);
|
|
22631
23802
|
function _next(value) {
|
|
22632
|
-
asyncGeneratorStep3(gen,
|
|
23803
|
+
asyncGeneratorStep3(gen, resolve3, reject, _next, _throw, "next", value);
|
|
22633
23804
|
}
|
|
22634
23805
|
function _throw(err) {
|
|
22635
|
-
asyncGeneratorStep3(gen,
|
|
23806
|
+
asyncGeneratorStep3(gen, resolve3, reject, _next, _throw, "throw", err);
|
|
22636
23807
|
}
|
|
22637
23808
|
_next(void 0);
|
|
22638
23809
|
});
|
|
@@ -22646,7 +23817,7 @@ function _readChunk() {
|
|
|
22646
23817
|
return _regeneratorRuntime3().wrap(function _callee2$(_context2) {
|
|
22647
23818
|
while (1) switch (_context2.prev = _context2.next) {
|
|
22648
23819
|
case 0:
|
|
22649
|
-
return _context2.abrupt("return", new Promise(function(
|
|
23820
|
+
return _context2.abrupt("return", new Promise(function(resolve3, reject) {
|
|
22650
23821
|
var onError = function onError2(err) {
|
|
22651
23822
|
stream.off("readable", onReadable);
|
|
22652
23823
|
reject(err);
|
|
@@ -22656,7 +23827,7 @@ function _readChunk() {
|
|
|
22656
23827
|
if (chunk2 !== null) {
|
|
22657
23828
|
stream.off("error", onError);
|
|
22658
23829
|
stream.off("readable", onReadable2);
|
|
22659
|
-
|
|
23830
|
+
resolve3(chunk2);
|
|
22660
23831
|
}
|
|
22661
23832
|
};
|
|
22662
23833
|
stream.once("error", onError);
|
|
@@ -22768,7 +23939,7 @@ var StreamSource = /* @__PURE__ */ (function() {
|
|
|
22768
23939
|
}]);
|
|
22769
23940
|
})();
|
|
22770
23941
|
|
|
22771
|
-
//
|
|
23942
|
+
// ../../node_modules/tus-js-client/lib.esm/node/fileReader.js
|
|
22772
23943
|
function _typeof7(o) {
|
|
22773
23944
|
"@babel/helpers - typeof";
|
|
22774
23945
|
return _typeof7 = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(o2) {
|
|
@@ -22836,20 +24007,20 @@ var FileReader = /* @__PURE__ */ (function() {
|
|
|
22836
24007
|
}]);
|
|
22837
24008
|
})();
|
|
22838
24009
|
|
|
22839
|
-
//
|
|
22840
|
-
import { createHash as
|
|
24010
|
+
// ../../node_modules/tus-js-client/lib.esm/node/fileSignature.js
|
|
24011
|
+
import { createHash as createHash4 } from "crypto";
|
|
22841
24012
|
import * as fs from "fs";
|
|
22842
24013
|
import * as path from "path";
|
|
22843
24014
|
function fingerprint(file, options) {
|
|
22844
24015
|
if (Buffer.isBuffer(file)) {
|
|
22845
24016
|
var blockSize = 64 * 1024;
|
|
22846
24017
|
var content = file.slice(0, Math.min(blockSize, file.length));
|
|
22847
|
-
var hash =
|
|
24018
|
+
var hash = createHash4("md5").update(content).digest("hex");
|
|
22848
24019
|
var ret = ["node-buffer", hash, file.length, options.endpoint].join("-");
|
|
22849
24020
|
return Promise.resolve(ret);
|
|
22850
24021
|
}
|
|
22851
24022
|
if (file instanceof fs.ReadStream && file.path != null) {
|
|
22852
|
-
return new Promise(function(
|
|
24023
|
+
return new Promise(function(resolve3, reject) {
|
|
22853
24024
|
var name = path.resolve(file.path);
|
|
22854
24025
|
fs.stat(file.path, function(err, info) {
|
|
22855
24026
|
if (err) {
|
|
@@ -22857,14 +24028,14 @@ function fingerprint(file, options) {
|
|
|
22857
24028
|
return;
|
|
22858
24029
|
}
|
|
22859
24030
|
var ret2 = ["node-file", name, info.size, info.mtime.getTime(), options.endpoint].join("-");
|
|
22860
|
-
|
|
24031
|
+
resolve3(ret2);
|
|
22861
24032
|
});
|
|
22862
24033
|
});
|
|
22863
24034
|
}
|
|
22864
24035
|
return Promise.resolve(null);
|
|
22865
24036
|
}
|
|
22866
24037
|
|
|
22867
|
-
//
|
|
24038
|
+
// ../../node_modules/tus-js-client/lib.esm/node/httpStack.js
|
|
22868
24039
|
var import_lodash = __toESM(require_lodash());
|
|
22869
24040
|
import * as http from "http";
|
|
22870
24041
|
import * as https from "https";
|
|
@@ -23049,7 +24220,7 @@ var Request = /* @__PURE__ */ (function() {
|
|
|
23049
24220
|
value: function send() {
|
|
23050
24221
|
var _this = this;
|
|
23051
24222
|
var body = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : null;
|
|
23052
|
-
return new Promise(function(
|
|
24223
|
+
return new Promise(function(resolve3, reject) {
|
|
23053
24224
|
var options = _objectSpread2(_objectSpread2(_objectSpread2({}, parse(_this._url)), _this._requestOptions), {}, {
|
|
23054
24225
|
method: _this._method,
|
|
23055
24226
|
headers: _objectSpread2(_objectSpread2({}, _this._requestOptions.headers || {}), _this._headers)
|
|
@@ -23067,7 +24238,7 @@ var Request = /* @__PURE__ */ (function() {
|
|
|
23067
24238
|
});
|
|
23068
24239
|
res.on("end", function() {
|
|
23069
24240
|
var responseText = Buffer.concat(resChunks).toString("utf8");
|
|
23070
|
-
|
|
24241
|
+
resolve3(new Response(res, responseText));
|
|
23071
24242
|
});
|
|
23072
24243
|
});
|
|
23073
24244
|
req.on("error", function(err) {
|
|
@@ -23168,11 +24339,11 @@ var writeBufferToStreamWithProgress = function writeBufferToStreamWithProgress2(
|
|
|
23168
24339
|
writeNextChunk();
|
|
23169
24340
|
};
|
|
23170
24341
|
|
|
23171
|
-
//
|
|
24342
|
+
// ../../node_modules/tus-js-client/lib.esm/node/urlStorage.js
|
|
23172
24343
|
var import_combine_errors = __toESM(require_combine_errors());
|
|
23173
24344
|
var lockfile = __toESM(require_proper_lockfile());
|
|
23174
24345
|
|
|
23175
|
-
//
|
|
24346
|
+
// ../../node_modules/tus-js-client/lib.esm/node/index.js
|
|
23176
24347
|
function _typeof9(o) {
|
|
23177
24348
|
"@babel/helpers - typeof";
|
|
23178
24349
|
return _typeof9 = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(o2) {
|
|
@@ -23322,9 +24493,9 @@ var Upload = /* @__PURE__ */ (function(_BaseUpload) {
|
|
|
23322
24493
|
var BUNNY_TUS_ENDPOINT = "https://video.bunnycdn.com/tusupload";
|
|
23323
24494
|
var CHUNK_SIZE = 50 * 1024 * 1024;
|
|
23324
24495
|
async function uploadVideoToBunny(params) {
|
|
23325
|
-
const { size } = await
|
|
24496
|
+
const { size } = await stat4(params.filePath);
|
|
23326
24497
|
const stream = createReadStream2(params.filePath);
|
|
23327
|
-
await new Promise((
|
|
24498
|
+
await new Promise((resolve3, reject) => {
|
|
23328
24499
|
const upload = new Upload(stream, {
|
|
23329
24500
|
endpoint: BUNNY_TUS_ENDPOINT,
|
|
23330
24501
|
uploadSize: size,
|
|
@@ -23345,7 +24516,7 @@ async function uploadVideoToBunny(params) {
|
|
|
23345
24516
|
removeFingerprintOnSuccess: true,
|
|
23346
24517
|
onError: (error) => reject(error),
|
|
23347
24518
|
onProgress: (uploaded, total) => params.onProgress?.(uploaded, total),
|
|
23348
|
-
onSuccess: () =>
|
|
24519
|
+
onSuccess: () => resolve3()
|
|
23349
24520
|
});
|
|
23350
24521
|
upload.start();
|
|
23351
24522
|
});
|
|
@@ -23471,7 +24642,7 @@ function videoMimeFromName(name) {
|
|
|
23471
24642
|
return "video/mp4";
|
|
23472
24643
|
}
|
|
23473
24644
|
var VIDEO_READY_STATES = /* @__PURE__ */ new Set(["ready", "failed"]);
|
|
23474
|
-
var sleep = (ms) => new Promise((
|
|
24645
|
+
var sleep = (ms) => new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
23475
24646
|
function registerMediaCommands(program2) {
|
|
23476
24647
|
const media = program2.command("media").description("Upload files to the workspace media library");
|
|
23477
24648
|
media.command("upload").description("Upload a file (image, etc.) to the media library; prints its public URL").requiredOption("--file <path>", "path to the file to upload").option("--name <name>", "display name (defaults to the file name)").option("--mime <type>", "MIME type (inferred from the file extension by default)").option("--folder <folder>", "media library folder name").option("--description <text>", "description stored on the media item").action(async (options, command) => {
|
|
@@ -23479,7 +24650,7 @@ function registerMediaCommands(program2) {
|
|
|
23479
24650
|
if (!options.file) {
|
|
23480
24651
|
throw new Error("Provide a file via --file <path>.");
|
|
23481
24652
|
}
|
|
23482
|
-
const bytes = await
|
|
24653
|
+
const bytes = await readFile3(options.file);
|
|
23483
24654
|
const name = options.name ?? basename(options.file);
|
|
23484
24655
|
const input = {
|
|
23485
24656
|
name,
|
|
@@ -23502,7 +24673,7 @@ function registerMediaCommands(program2) {
|
|
|
23502
24673
|
if (!options.file) {
|
|
23503
24674
|
throw new Error("Provide a video file via --file <path>.");
|
|
23504
24675
|
}
|
|
23505
|
-
const { size } = await
|
|
24676
|
+
const { size } = await stat5(options.file);
|
|
23506
24677
|
const title = options.title ?? basename(options.file);
|
|
23507
24678
|
const mimeType = options.mime ?? videoMimeFromName(options.file);
|
|
23508
24679
|
const client = createApiClient({ apiKey: flags.apiKey, verbose: flags.verbose });
|
|
@@ -24223,8 +25394,8 @@ Next: \`newsletters issues draft save ${productId} ${issue.id} --content <markdo
|
|
|
24223
25394
|
);
|
|
24224
25395
|
if (report) {
|
|
24225
25396
|
printTable(
|
|
24226
|
-
Object.entries(report.stats).map(([
|
|
24227
|
-
stat:
|
|
25397
|
+
Object.entries(report.stats).map(([stat6, value]) => ({
|
|
25398
|
+
stat: stat6,
|
|
24228
25399
|
value: String(value ?? "")
|
|
24229
25400
|
})),
|
|
24230
25401
|
["stat", "value"]
|
|
@@ -24818,7 +25989,8 @@ Prices (${checkout.prices.length}):
|
|
|
24818
25989
|
hidden: yesNo(p.hidden),
|
|
24819
25990
|
saleStartsAt: p.saleStartsAt ?? "",
|
|
24820
25991
|
saleEndsAt: p.saleEndsAt ?? "",
|
|
24821
|
-
|
|
25992
|
+
// A stored 0 sells without limit, same as no limit at all.
|
|
25993
|
+
quantityLimit: p.quantityLimit || "unlimited"
|
|
24822
25994
|
})),
|
|
24823
25995
|
[
|
|
24824
25996
|
"id",
|
|
@@ -25572,10 +26744,33 @@ function registerOtoCommands(program2) {
|
|
|
25572
26744
|
|
|
25573
26745
|
// src/commands/pages/index.ts
|
|
25574
26746
|
import { existsSync as existsSync3, readFileSync as readFileSync21 } from "fs";
|
|
25575
|
-
import { readFile as
|
|
26747
|
+
import { readFile as readFile4 } from "fs/promises";
|
|
26748
|
+
var CLEAR_VALUE = "none";
|
|
26749
|
+
var FAMILY_STEPS = ["follow-ups", "thank-you-page", "room-page"];
|
|
26750
|
+
function parseClearableLink(value, flag) {
|
|
26751
|
+
const trimmed = value.trim();
|
|
26752
|
+
if (trimmed === CLEAR_VALUE) return null;
|
|
26753
|
+
if (!/^https?:\/\/\S+$/i.test(trimmed)) {
|
|
26754
|
+
throw new Error(
|
|
26755
|
+
`Invalid ${flag}: ${value} (expected an http(s) link, or ${CLEAR_VALUE} to clear)`
|
|
26756
|
+
);
|
|
26757
|
+
}
|
|
26758
|
+
return trimmed;
|
|
26759
|
+
}
|
|
26760
|
+
function buildEventInput(options) {
|
|
26761
|
+
const event = {
|
|
26762
|
+
...options.eventStart !== void 0 ? { startAt: options.eventStart } : {},
|
|
26763
|
+
...options.eventTimezone !== void 0 ? { timezone: options.eventTimezone } : {},
|
|
26764
|
+
...options.eventDuration !== void 0 ? { durationMinutes: parsePositiveInt14(options.eventDuration, "--event-duration") } : {},
|
|
26765
|
+
...options.joinUrl !== void 0 ? { joinUrl: parseClearableLink(options.joinUrl, "--join-url") } : {},
|
|
26766
|
+
...options.replayUrl !== void 0 ? { replayUrl: parseClearableLink(options.replayUrl, "--replay-url") } : {},
|
|
26767
|
+
...options.liveRoom !== void 0 ? { liveRoomId: options.liveRoom.trim() === CLEAR_VALUE ? null : options.liveRoom.trim() } : {}
|
|
26768
|
+
};
|
|
26769
|
+
return Object.keys(event).length > 0 ? event : void 0;
|
|
26770
|
+
}
|
|
25576
26771
|
async function resolveHtml2(options) {
|
|
25577
26772
|
if (options.file) {
|
|
25578
|
-
return
|
|
26773
|
+
return readFile4(options.file, "utf8");
|
|
25579
26774
|
}
|
|
25580
26775
|
return options.html;
|
|
25581
26776
|
}
|
|
@@ -25759,7 +26954,7 @@ function registerPagesCommands(program2) {
|
|
|
25759
26954
|
});
|
|
25760
26955
|
});
|
|
25761
26956
|
pages.command("update").description(
|
|
25762
|
-
"Update a page (slug, title, HTML body, cookie-banner visibility,
|
|
26957
|
+
"Update a page (slug, title, HTML body, cookie-banner visibility, its tracking tools, or a webinar page's date and links). A new --event-start moves the reminder ladder with it (send moments, the date in the copy, rungs already scheduled); --join-url changes the watch page's button at once."
|
|
25763
26958
|
).argument("<id>", "page id").option("--slug <slug>", "new slug").option("--title <title>", "new title").option("--file <path>", "path to a new .html file").option("--html <html>", "new inline HTML body").option("--cookie-banner", "show the workspace cookie banner on this page again").option(
|
|
25764
26959
|
"--no-cookie-banner",
|
|
25765
26960
|
"hide the workspace cookie banner on this page (pixels still wait for consent given elsewhere)"
|
|
@@ -25769,9 +26964,16 @@ function registerPagesCommands(program2) {
|
|
|
25769
26964
|
).option(
|
|
25770
26965
|
"--tracking-integrations <ids>",
|
|
25771
26966
|
"comma-separated tracking integration ids for --tracking selected (one per platform)"
|
|
26967
|
+
).option("--event-start <iso>", "webinar: start as ISO 8601 WITH offset (moves the reminders)").option("--event-timezone <tz>", "webinar: IANA timezone, with --event-start").option("--event-duration <minutes>", "webinar: length in minutes, with --event-start").option(
|
|
26968
|
+
"--join-url <url>",
|
|
26969
|
+
`webinar: the meeting link (ClickMeeting, Zoom, Teams, Meet); ${CLEAR_VALUE} clears it`
|
|
26970
|
+
).option("--replay-url <url>", `webinar: the recording link; ${CLEAR_VALUE} clears it`).option(
|
|
26971
|
+
"--live-room <roomId>",
|
|
26972
|
+
`webinar: run it on a webinar from \`live-rooms list\` (live_rooms plan feature); ${CLEAR_VALUE} detaches`
|
|
25772
26973
|
).action(async (id, options, command) => {
|
|
25773
26974
|
const flags = getGlobalFlags(command);
|
|
25774
26975
|
const html = await resolveHtml2(options);
|
|
26976
|
+
const event = buildEventInput(options);
|
|
25775
26977
|
const trackingIds = options.trackingIntegrations?.split(",").map((value) => value.trim()).filter((value) => value.length > 0);
|
|
25776
26978
|
const trackingMode = options.tracking !== void 0 ? parseChoice5(options.tracking, TRACKING_MODES, "--tracking") : trackingIds !== void 0 ? "selected" : void 0;
|
|
25777
26979
|
const input = {
|
|
@@ -25784,7 +26986,8 @@ function registerPagesCommands(program2) {
|
|
|
25784
26986
|
mode: trackingMode,
|
|
25785
26987
|
...trackingIds !== void 0 ? { integrationIds: trackingIds } : {}
|
|
25786
26988
|
}
|
|
25787
|
-
} : {}
|
|
26989
|
+
} : {},
|
|
26990
|
+
...event ? { event } : {}
|
|
25788
26991
|
};
|
|
25789
26992
|
const client = createApiClient({ apiKey: flags.apiKey, verbose: flags.verbose });
|
|
25790
26993
|
const result = await client.updatePage(id, input);
|
|
@@ -25793,24 +26996,42 @@ function registerPagesCommands(program2) {
|
|
|
25793
26996
|
`);
|
|
25794
26997
|
});
|
|
25795
26998
|
});
|
|
25796
|
-
|
|
25797
|
-
|
|
25798
|
-
|
|
25799
|
-
|
|
25800
|
-
|
|
25801
|
-
|
|
25802
|
-
|
|
25803
|
-
|
|
25804
|
-
|
|
25805
|
-
|
|
25806
|
-
|
|
25807
|
-
|
|
25808
|
-
const result = await client.publishPage(id, false);
|
|
25809
|
-
emit(flags, result, () => {
|
|
25810
|
-
process.stdout.write(`Page ${result.pageId} is now ${result.status}
|
|
26999
|
+
const publishCommand = (verb, publish, description) => {
|
|
27000
|
+
pages.command(verb).description(description).argument("<id>", "page id").option("--only-this-page", "change this page only, not its companions or site pages").option("--dry-run", "list the pages that would change; change nothing").action(
|
|
27001
|
+
async (id, options, command) => {
|
|
27002
|
+
const flags = getGlobalFlags(command);
|
|
27003
|
+
const client = createApiClient({ apiKey: flags.apiKey, verbose: flags.verbose });
|
|
27004
|
+
const result = await client.publishPage(id, publish, {
|
|
27005
|
+
...options.onlyThisPage ? { scope: "page" } : {},
|
|
27006
|
+
...options.dryRun ? { dryRun: true } : {}
|
|
27007
|
+
});
|
|
27008
|
+
emit(flags, result, () => {
|
|
27009
|
+
if (!result.dryRun) {
|
|
27010
|
+
process.stdout.write(`Page ${result.pageId} is now ${result.status}
|
|
25811
27011
|
`);
|
|
25812
|
-
|
|
25813
|
-
|
|
27012
|
+
return;
|
|
27013
|
+
}
|
|
27014
|
+
const changes = result.changes ?? [];
|
|
27015
|
+
process.stdout.write(
|
|
27016
|
+
changes.length === 0 ? "Dry run: nothing would change.\n" : `Dry run: ${changes.length} page(s) would change (nothing changed):
|
|
27017
|
+
`
|
|
27018
|
+
);
|
|
27019
|
+
for (const change of changes) {
|
|
27020
|
+
process.stdout.write(
|
|
27021
|
+
` ${change.pageId} /${change.slug} "${change.title}": ${change.status} -> ${change.nextStatus}
|
|
27022
|
+
`
|
|
27023
|
+
);
|
|
27024
|
+
}
|
|
27025
|
+
});
|
|
27026
|
+
}
|
|
27027
|
+
);
|
|
27028
|
+
};
|
|
27029
|
+
publishCommand("publish", true, "Publish a page (a site's home page publishes the whole site)");
|
|
27030
|
+
publishCommand(
|
|
27031
|
+
"unpublish",
|
|
27032
|
+
false,
|
|
27033
|
+
"Revert a page to draft (a site's home page reverts the whole site)"
|
|
27034
|
+
);
|
|
25814
27035
|
pages.command("delete").description("Delete a page").argument("<id>", "page id").action(async (id, _, command) => {
|
|
25815
27036
|
const flags = getGlobalFlags(command);
|
|
25816
27037
|
const client = createApiClient({ apiKey: flags.apiKey, verbose: flags.verbose });
|
|
@@ -25848,7 +27069,10 @@ function registerPagesCommands(program2) {
|
|
|
25848
27069
|
).option("--slug <slug>", "public URL slug; derived from the title when omitted").option(
|
|
25849
27070
|
"--delivery-url <url>",
|
|
25850
27071
|
"what the signup delivers (file URL / join link) \u2014 never printed"
|
|
25851
|
-
).option("--delivery-name <name>", "human name of the deliverable for the delivery email").option("--event-start <iso>", "webinar: event start as ISO 8601 WITH offset").option("--event-timezone <tz>", "webinar: IANA timezone, e.g. Europe/Warsaw").option("--event-duration <minutes>", "webinar: event length in minutes (default 60)").option(
|
|
27072
|
+
).option("--delivery-name <name>", "human name of the deliverable for the delivery email").option("--event-start <iso>", "webinar: event start as ISO 8601 WITH offset").option("--event-timezone <tz>", "webinar: IANA timezone, e.g. Europe/Warsaw").option("--event-duration <minutes>", "webinar: event length in minutes (default 60)").option(
|
|
27073
|
+
"--event-mode <mode>",
|
|
27074
|
+
`webinar: ${EVENT_MODES2.join(" | ")} (on_demand = on-demand webinar, no date)`
|
|
27075
|
+
).option("--replay-media <mediaId>", "on-demand webinar: Media Library video the room plays").option(
|
|
25852
27076
|
"--opt-in-mode <mode>",
|
|
25853
27077
|
`signup form opt-in (${OPT_IN_MODES2.join(" | ")}; default single)`
|
|
25854
27078
|
).option("--form-tags <ids>", "comma-separated tag ids the auto-provisioned form applies").option(
|
|
@@ -25889,6 +27113,14 @@ function registerPagesCommands(program2) {
|
|
|
25889
27113
|
emit(flags, result, () => {
|
|
25890
27114
|
process.stdout.write(`Edited page ${pageId}.
|
|
25891
27115
|
`);
|
|
27116
|
+
const removed = result.removedCode;
|
|
27117
|
+
if (removed) {
|
|
27118
|
+
const sources = removed.scriptSources.length > 0 ? ` (${removed.scriptSources.join("; ")})` : "";
|
|
27119
|
+
process.stdout.write(
|
|
27120
|
+
` Removed on save, NOT on the page: ${removed.scripts} script(s)${sources}, ${removed.handlers} event handler(s), ${removed.jsUrls} javascript: link(s). Put a third-party embed in the HTML yourself (\`pages update ${pageId} --file \u2026\`).
|
|
27121
|
+
`
|
|
27122
|
+
);
|
|
27123
|
+
}
|
|
25892
27124
|
process.stdout.write(
|
|
25893
27125
|
` Previous HTML kept as the newest entry of \`pages revisions ${pageId}\` \u2014 undo with \`pages restore ${pageId} <revisionId> --confirm\`.
|
|
25894
27126
|
`
|
|
@@ -25946,6 +27178,20 @@ function registerPagesCommands(program2) {
|
|
|
25946
27178
|
});
|
|
25947
27179
|
}
|
|
25948
27180
|
);
|
|
27181
|
+
pages.command("family-step").description(
|
|
27182
|
+
"Build ONE missing piece of a page's family (the editor checklist's rows): follow-ups = the reminder ladder of a dated webinar page (e.g. your own page that got its date with `pages update --event-start`), thank-you-page / room-page (the watch page) = AI generations (credits). Runs in the background (1-5 min); the ladder lands as DRAFTS that `pages activate-automations` then schedules."
|
|
27183
|
+
).argument("<pageId>", "the signup / webinar registration page id").requiredOption("--step <step>", `which piece (${FAMILY_STEPS.join(" | ")})`).action(async (pageId, options, command) => {
|
|
27184
|
+
const flags = getGlobalFlags(command);
|
|
27185
|
+
const step = parseChoice5(options.step, FAMILY_STEPS, "--step");
|
|
27186
|
+
const client = createApiClient({ apiKey: flags.apiKey, verbose: flags.verbose });
|
|
27187
|
+
const result = await client.startPageFamilyStep(pageId, { step });
|
|
27188
|
+
emit(flags, result, () => {
|
|
27189
|
+
process.stdout.write(
|
|
27190
|
+
`Queued ${step} for page ${pageId} (run ${result.runId}). It takes 1-5 minutes` + (step === "follow-ups" ? `; then run "pages activate-automations ${pageId} --confirm" to schedule the reminders.
|
|
27191
|
+
` : ".\n")
|
|
27192
|
+
);
|
|
27193
|
+
});
|
|
27194
|
+
});
|
|
25949
27195
|
pages.command("activate-automations").description(
|
|
25950
27196
|
"One-click activation of everything generated around a page: publishes the DRAFT delivery automation and schedules the webinar follow-up broadcasts at their stored send times. Emails will go out for real; items whose copy still carries [FILL IN] placeholders are skipped, never sent. Requires --confirm."
|
|
25951
27197
|
).argument("<pageId>", "page id").option("--confirm", "required \u2014 acknowledge that automations go live and emails will send").action(async (pageId, options, command) => {
|
|
@@ -25981,7 +27227,7 @@ function registerPagesCommands(program2) {
|
|
|
25981
27227
|
|
|
25982
27228
|
// src/commands/products/index.ts
|
|
25983
27229
|
import { readFileSync as readFileSync22 } from "fs";
|
|
25984
|
-
import { readFile as
|
|
27230
|
+
import { readFile as readFile5 } from "fs/promises";
|
|
25985
27231
|
import { basename as basename2 } from "path";
|
|
25986
27232
|
var KB_TREE_CHANGED_ERROR = "tree-changed";
|
|
25987
27233
|
var JSON_FLAG_HINT3 = "(write it as --json=<value> \u2014 a bare --json is the global JSON-output flag)";
|
|
@@ -26116,7 +27362,7 @@ function collectRepeatable6(value, previous) {
|
|
|
26116
27362
|
}
|
|
26117
27363
|
var HTTP_URL_PATTERN2 = /^https?:\/\//i;
|
|
26118
27364
|
async function uploadLocalFile(client, filePath) {
|
|
26119
|
-
const bytes = await
|
|
27365
|
+
const bytes = await readFile5(filePath);
|
|
26120
27366
|
const name = basename2(filePath);
|
|
26121
27367
|
const result = await client.uploadMedia({
|
|
26122
27368
|
name,
|
|
@@ -26465,7 +27711,7 @@ function registerProductsCommands(program2) {
|
|
|
26465
27711
|
}
|
|
26466
27712
|
);
|
|
26467
27713
|
plans.command("backfill").description(
|
|
26468
|
-
"Eagerly add plan-bundled customers to a community \u2014 creates member docs for every active customer whose effective plan includes the community. Requires --confirm."
|
|
27714
|
+
"Eagerly add plan-bundled customers to a community \u2014 starts a background run that creates member docs for every active customer whose effective plan includes the community. Requires --confirm."
|
|
26469
27715
|
).argument("<productId>", "the parent (bundling) product id").requiredOption(
|
|
26470
27716
|
"--community <communityProductId>",
|
|
26471
27717
|
"the community PRODUCT id to backfill members into"
|
|
@@ -26480,8 +27726,12 @@ function registerProductsCommands(program2) {
|
|
|
26480
27726
|
const client = createApiClient({ apiKey: flags.apiKey, verbose: flags.verbose });
|
|
26481
27727
|
const result = await client.backfillPlanCommunityMembers(productId, options.community);
|
|
26482
27728
|
emit(flags, result, () => {
|
|
27729
|
+
if (result.status === "no-eligible-clients") {
|
|
27730
|
+
process.stdout.write("Nothing to backfill: no active customer is on these plans.\n");
|
|
27731
|
+
return;
|
|
27732
|
+
}
|
|
26483
27733
|
process.stdout.write(
|
|
26484
|
-
|
|
27734
|
+
(result.status === "already-running" ? "The same backfill is already running" : "Backfill started in the background") + ` for ${result.eligibleClients} eligible customers (run ${result.runId}). Members appear as the run goes; customers without an auth user yet are skipped (the lazy backstop covers them).
|
|
26485
27735
|
`
|
|
26486
27736
|
);
|
|
26487
27737
|
});
|
|
@@ -26606,7 +27856,7 @@ function registerProductsCommands(program2) {
|
|
|
26606
27856
|
if (options.deliveryTime === void 0 !== (options.deliveryUnit === void 0)) {
|
|
26607
27857
|
throw new Error("Pass --delivery-time and --delivery-unit together.");
|
|
26608
27858
|
}
|
|
26609
|
-
const
|
|
27859
|
+
const duration2 = options.duration !== void 0 ? parseNonNegativeInt3(options.duration, "--duration") : void 0;
|
|
26610
27860
|
const deliveryTimeValue = options.deliveryTime !== void 0 ? parseNonNegativeInt3(options.deliveryTime, "--delivery-time") : void 0;
|
|
26611
27861
|
const capacity = options.capacity !== void 0 ? parseNonNegativeInt3(options.capacity, "--capacity") : void 0;
|
|
26612
27862
|
let revisionRounds;
|
|
@@ -26630,7 +27880,7 @@ function registerProductsCommands(program2) {
|
|
|
26630
27880
|
const config2 = {
|
|
26631
27881
|
bookingMethod: bookingMethod ?? base2.bookingMethod ?? "external",
|
|
26632
27882
|
bookingUrl: options.bookingUrl ?? base2.bookingUrl ?? null,
|
|
26633
|
-
duration:
|
|
27883
|
+
duration: duration2 ?? base2.duration ?? 0,
|
|
26634
27884
|
location: options.location ?? base2.location ?? "",
|
|
26635
27885
|
locationDetails: options.locationDetails ?? base2.locationDetails ?? null,
|
|
26636
27886
|
confirmationMessage: options.confirmationMessage ?? base2.confirmationMessage ?? null,
|
|
@@ -27158,6 +28408,10 @@ var PROGRAM_KINDS = ["referral", "affiliate"];
|
|
|
27158
28408
|
var WRITABLE_STATUSES = ["active", "inactive"];
|
|
27159
28409
|
var ENROLLMENT_MODES = ["open", "inviteOnly", "closed"];
|
|
27160
28410
|
var REWARD_DECISIONS = ["approve", "reject"];
|
|
28411
|
+
var SWITCH_VALUES = ["on", "off"];
|
|
28412
|
+
function parseSwitch(value, flag) {
|
|
28413
|
+
return parseChoice6(value, SWITCH_VALUES, flag) === "on";
|
|
28414
|
+
}
|
|
27161
28415
|
function parseChoice6(value, allowed, flag) {
|
|
27162
28416
|
const match = allowed.find((candidate) => candidate === value);
|
|
27163
28417
|
if (match === void 0) {
|
|
@@ -27238,6 +28492,10 @@ function printProgram(program2) {
|
|
|
27238
28492
|
`);
|
|
27239
28493
|
process.stdout.write(` Products: ${program2.productIds.join(", ")}
|
|
27240
28494
|
`);
|
|
28495
|
+
if (program2.kind === "referral") {
|
|
28496
|
+
process.stdout.write(` Form signups count: ${program2.countFormSignups ? "on" : "off"}
|
|
28497
|
+
`);
|
|
28498
|
+
}
|
|
27241
28499
|
process.stdout.write(
|
|
27242
28500
|
` Attribution: ${program2.attribution.windowDays}-day window, coupon attribution ${program2.attribution.allowCouponAttribution ? "on" : "off"}, two-sided discount ${program2.attribution.twoSidedDiscountId ?? "none"}
|
|
27243
28501
|
`
|
|
@@ -27298,6 +28556,9 @@ function buildCreateInput(options) {
|
|
|
27298
28556
|
if (options.status !== void 0) {
|
|
27299
28557
|
input.status = parseChoice6(options.status, WRITABLE_STATUSES, "--status");
|
|
27300
28558
|
}
|
|
28559
|
+
if (options.countFormSignups !== void 0) {
|
|
28560
|
+
input.countFormSignups = parseSwitch(options.countFormSignups, "--count-form-signups");
|
|
28561
|
+
}
|
|
27301
28562
|
return input;
|
|
27302
28563
|
}
|
|
27303
28564
|
function buildUpdateInput2(options) {
|
|
@@ -27310,9 +28571,12 @@ function buildUpdateInput2(options) {
|
|
|
27310
28571
|
if (options.enrollment !== void 0) {
|
|
27311
28572
|
input.enrollment = parseChoice6(options.enrollment, ENROLLMENT_MODES, "--enrollment");
|
|
27312
28573
|
}
|
|
28574
|
+
if (options.countFormSignups !== void 0) {
|
|
28575
|
+
input.countFormSignups = parseSwitch(options.countFormSignups, "--count-form-signups");
|
|
28576
|
+
}
|
|
27313
28577
|
if (Object.keys(input).length === 0) {
|
|
27314
28578
|
throw new Error(
|
|
27315
|
-
"Nothing to update \u2014 pass --name, --status, --product, --enrollment or --json <config>."
|
|
28579
|
+
"Nothing to update \u2014 pass --name, --status, --product, --enrollment, --count-form-signups or --json <config>."
|
|
27316
28580
|
);
|
|
27317
28581
|
}
|
|
27318
28582
|
return input;
|
|
@@ -27420,6 +28684,9 @@ Failed deliveries (${result.failedDeliveries.length})
|
|
|
27420
28684
|
referrals.command("create").description(
|
|
27421
28685
|
"Create a program over one or more products (both kinds need the Growth plan). Deep config (milestones, commission, attribution) goes in --json; flat flags win over --json keys."
|
|
27422
28686
|
).requiredOption("--kind <kind>", `program kind (${PROGRAM_KINDS.join(" | ")})`).requiredOption("--name <name>", "program name").requiredOption("--product <ids>", "comma-separated product ids whose purchases count").option("--enrollment <mode>", `how participants join (${ENROLLMENT_MODES.join(" | ")})`).option("--status <status>", `initial status (${WRITABLE_STATUSES.join(" | ")})`).option(
|
|
28687
|
+
"--count-form-signups <on|off>",
|
|
28688
|
+
"referral kind: confirmed form signups of new contacts with a participant code count too (default off)"
|
|
28689
|
+
).option(
|
|
27423
28690
|
"--json <fileOrJson>",
|
|
27424
28691
|
"inline JSON object or .json path with `milestones`, `commission` and/or `attribution` (write it as --json=<value> \u2014 a bare --json is the global JSON-output flag)"
|
|
27425
28692
|
).action(async (options, command) => {
|
|
@@ -27435,6 +28702,9 @@ Failed deliveries (${result.failedDeliveries.length})
|
|
|
27435
28702
|
referrals.command("update").description(
|
|
27436
28703
|
"Update a program. --product and --json `milestones` are FULL replaces (read the program first); flat flags win over --json keys. Activating (--status active) needs the Growth plan. Use `referrals archive` to archive."
|
|
27437
28704
|
).argument("<programId>", "program id").option("--name <name>", "program name").option("--status <status>", `status (${WRITABLE_STATUSES.join(" | ")})`).option("--product <ids>", "comma-separated product ids (full replace)").option("--enrollment <mode>", `how participants join (${ENROLLMENT_MODES.join(" | ")})`).option(
|
|
28705
|
+
"--count-form-signups <on|off>",
|
|
28706
|
+
"referral kind: confirmed form signups of new contacts with a participant code count too (default off)"
|
|
28707
|
+
).option(
|
|
27438
28708
|
"--json <fileOrJson>",
|
|
27439
28709
|
"inline JSON object or .json path with `milestones`, `commission` and/or `attribution` (write it as --json=<value> \u2014 a bare --json is the global JSON-output flag)"
|
|
27440
28710
|
).action(async (programId, options, command) => {
|
|
@@ -27541,9 +28811,9 @@ function parseMinorUnits(value, flag) {
|
|
|
27541
28811
|
}
|
|
27542
28812
|
return n;
|
|
27543
28813
|
}
|
|
27544
|
-
var CONDITION_FLAGS_HELP = "--has-tag/--without-tag, --has-any-tag/--has-none-tag, --owns/--not-owns, --owns-direct/--not-owns-direct, --spent-gte/--spent-lte, --subscription, or --json-file";
|
|
28814
|
+
var CONDITION_FLAGS_HELP = "--has-tag/--without-tag, --has-any-tag/--has-all-tag/--has-none-tag, --owns/--not-owns, --owns-direct/--not-owns-direct, --spent-gte/--spent-lte, --subscription, --referral-participant/--not-referral-participant, or --json-file";
|
|
27545
28815
|
function resolveConditions(options) {
|
|
27546
|
-
const hasFlagConditions = options.hasTag.length > 0 || options.withoutTag.length > 0 || options.hasAnyTag.length > 0 || options.hasNoneTag.length > 0 || options.owns.length > 0 || options.notOwns.length > 0 || options.ownsDirect.length > 0 || options.notOwnsDirect.length > 0 || options.spentGte !== void 0 || options.spentLte !== void 0 || options.subscription !== void 0;
|
|
28816
|
+
const hasFlagConditions = options.hasTag.length > 0 || options.withoutTag.length > 0 || options.hasAnyTag.length > 0 || options.hasAllTag.length > 0 || options.hasNoneTag.length > 0 || options.owns.length > 0 || options.notOwns.length > 0 || options.ownsDirect.length > 0 || options.notOwnsDirect.length > 0 || options.spentGte !== void 0 || options.spentLte !== void 0 || options.subscription !== void 0 || options.referralParticipant.length > 0 || options.notReferralParticipant.length > 0;
|
|
27547
28817
|
if (options.jsonFile !== void 0) {
|
|
27548
28818
|
if (hasFlagConditions) {
|
|
27549
28819
|
throw new Error("Use either condition flags or --json-file, not both.");
|
|
@@ -27573,6 +28843,9 @@ function resolveConditions(options) {
|
|
|
27573
28843
|
if (options.hasAnyTag.length > 0) {
|
|
27574
28844
|
conditions.push({ field: "tag", operator: "has-any", tagIds: options.hasAnyTag });
|
|
27575
28845
|
}
|
|
28846
|
+
if (options.hasAllTag.length > 0) {
|
|
28847
|
+
conditions.push({ field: "tag", operator: "has-all", tagIds: options.hasAllTag });
|
|
28848
|
+
}
|
|
27576
28849
|
if (options.hasNoneTag.length > 0) {
|
|
27577
28850
|
conditions.push({ field: "tag", operator: "has-none", tagIds: options.hasNoneTag });
|
|
27578
28851
|
}
|
|
@@ -27617,6 +28890,12 @@ function resolveConditions(options) {
|
|
|
27617
28890
|
if (options.notClicked !== void 0) {
|
|
27618
28891
|
conditions.push({ field: "campaign", operator: "not-clicked", campaignId: options.notClicked });
|
|
27619
28892
|
}
|
|
28893
|
+
for (const programId of options.referralParticipant) {
|
|
28894
|
+
conditions.push({ field: "referralProgram", operator: "participant", programId });
|
|
28895
|
+
}
|
|
28896
|
+
for (const programId of options.notReferralParticipant) {
|
|
28897
|
+
conditions.push({ field: "referralProgram", operator: "not-participant", programId });
|
|
28898
|
+
}
|
|
27620
28899
|
return conditions;
|
|
27621
28900
|
}
|
|
27622
28901
|
function withConditionOptions(command) {
|
|
@@ -27635,6 +28914,11 @@ function withConditionOptions(command) {
|
|
|
27635
28914
|
"client carries AT LEAST ONE of these tags (repeat the flag; one OR condition, max 10 ids) - one send to several tags without double-mailing",
|
|
27636
28915
|
collectRepeatable7,
|
|
27637
28916
|
[]
|
|
28917
|
+
).option(
|
|
28918
|
+
"--has-all-tag <tagId>",
|
|
28919
|
+
"client carries EVERY ONE of these tags (repeat the flag; one condition, max 10 ids)",
|
|
28920
|
+
collectRepeatable7,
|
|
28921
|
+
[]
|
|
27638
28922
|
).option(
|
|
27639
28923
|
"--has-none-tag <tagId>",
|
|
27640
28924
|
"client carries NONE of these tags (repeat the flag; one condition, max 10 ids)",
|
|
@@ -27672,6 +28956,16 @@ function withConditionOptions(command) {
|
|
|
27672
28956
|
).option(
|
|
27673
28957
|
"--not-clicked <campaignId>",
|
|
27674
28958
|
"received this SENT campaign and never clicked a link in it"
|
|
28959
|
+
).option(
|
|
28960
|
+
"--referral-participant <programId>",
|
|
28961
|
+
"client is an ACTIVE participant of this referral program (rprog_\u2026; blocked ones do not count; repeatable)",
|
|
28962
|
+
collectRepeatable7,
|
|
28963
|
+
[]
|
|
28964
|
+
).option(
|
|
28965
|
+
"--not-referral-participant <programId>",
|
|
28966
|
+
"client is NOT an active participant of this referral program (repeatable)",
|
|
28967
|
+
collectRepeatable7,
|
|
28968
|
+
[]
|
|
27675
28969
|
).option(
|
|
27676
28970
|
"--json-file <path>",
|
|
27677
28971
|
"read the conditions as a JSON array from a file instead of flags"
|
|
@@ -27681,7 +28975,12 @@ function describeCondition(condition) {
|
|
|
27681
28975
|
switch (condition.field) {
|
|
27682
28976
|
case "tag":
|
|
27683
28977
|
if ("tagIds" in condition) {
|
|
27684
|
-
|
|
28978
|
+
const listWords = {
|
|
28979
|
+
"has-any": "has any of",
|
|
28980
|
+
"has-all": "has all of",
|
|
28981
|
+
"has-none": "has none of"
|
|
28982
|
+
};
|
|
28983
|
+
return `${listWords[condition.operator]} tags ${condition.tagIds.join(", ")}`;
|
|
27685
28984
|
}
|
|
27686
28985
|
return `${condition.operator === "has" ? "has" : "lacks"} tag ${condition.tagId}`;
|
|
27687
28986
|
case "product":
|
|
@@ -27694,12 +28993,17 @@ function describeCondition(condition) {
|
|
|
27694
28993
|
return `last email engagement ${condition.operator === "within" ? "within" : "not within"} the last ${condition.days} days`;
|
|
27695
28994
|
case "lastOrderAt":
|
|
27696
28995
|
return `last paid order ${condition.operator === "within" ? "within" : "not within"} the last ${condition.days} days`;
|
|
28996
|
+
case "emailSubscribedAt":
|
|
28997
|
+
case "addedAt":
|
|
28998
|
+
return `${condition.field === "emailSubscribedAt" ? "subscribed to the newsletter" : "added"} ${condition.operator === "within" ? `in the last ${condition.days} days` : `at least ${condition.days} days ago`}`;
|
|
27697
28999
|
case "smsSubscription":
|
|
27698
29000
|
return `sms consent is ${condition.status}`;
|
|
27699
29001
|
case "phoneNumber":
|
|
27700
29002
|
return condition.operator === "has" ? "has a phone number" : "has no phone number";
|
|
27701
29003
|
case "campaign":
|
|
27702
29004
|
return `${condition.operator.replace("-", " ")} campaign ${condition.campaignId}`;
|
|
29005
|
+
case "referralProgram":
|
|
29006
|
+
return `${condition.operator === "participant" ? "takes part in" : "does not take part in"} referral program ${condition.programId}`;
|
|
27703
29007
|
case "email":
|
|
27704
29008
|
return `email ${condition.operator === "is-any" ? "is one of" : "is none of"} ${condition.emails.join(", ")} (set in the dashboard)`;
|
|
27705
29009
|
}
|
|
@@ -28059,7 +29363,7 @@ function withBrandingHint(error) {
|
|
|
28059
29363
|
}
|
|
28060
29364
|
var SECTION_UPDATED = (section) => `Updated site ${section} settings.
|
|
28061
29365
|
`;
|
|
28062
|
-
function
|
|
29366
|
+
function registerSiteCommands2(program2) {
|
|
28063
29367
|
const site = program2.command("site").description(
|
|
28064
29368
|
"Manage the public site \u2014 platform settings (general, theme, branding, main page, privacy, portal, access policy, communication), activation, slug, custom domains, storefront layout and legal documents"
|
|
28065
29369
|
);
|
|
@@ -28426,7 +29730,9 @@ function registerSiteCommands(program2) {
|
|
|
28426
29730
|
const result = await client.verifySiteDomains(list);
|
|
28427
29731
|
emit(flags, result, () => printDomainResults(result.results));
|
|
28428
29732
|
});
|
|
28429
|
-
const storefront = site.command("storefront").description(
|
|
29733
|
+
const storefront = site.command("storefront").description(
|
|
29734
|
+
"The product storefront layout: sections, preset, theme mode, profile hero, footer"
|
|
29735
|
+
);
|
|
28430
29736
|
storefront.command("get").description("Storefront config (sections table; `--json` for the full config)").action(async (_options, command) => {
|
|
28431
29737
|
const flags = getGlobalFlags(command);
|
|
28432
29738
|
const client = createApiClient({ apiKey: flags.apiKey, verbose: flags.verbose });
|
|
@@ -28437,7 +29743,7 @@ function registerSiteCommands(program2) {
|
|
|
28437
29743
|
"REPLACE the whole storefront config with --json=<file|inline> (read `site storefront get --json` first and edit that)"
|
|
28438
29744
|
).option(
|
|
28439
29745
|
"--json <fileOrJson>",
|
|
28440
|
-
"inline JSON object or .json path \u2014 sections (required), layout, columns, preset, mode, overrides, looseProductOrder, heading, subheading, footer"
|
|
29746
|
+
"inline JSON object or .json path \u2014 sections (required), layout, columns, preset, mode, overrides, looseProductOrder, heading, subheading, hero, footer"
|
|
28441
29747
|
).action(async (options, command) => {
|
|
28442
29748
|
const flags = getGlobalFlags(command);
|
|
28443
29749
|
if (options.json === void 0) {
|
|
@@ -28514,6 +29820,7 @@ function printStorefront(config) {
|
|
|
28514
29820
|
{ field: "columns", value: config.columns ?? "" },
|
|
28515
29821
|
{ field: "heading", value: config.heading ?? "" },
|
|
28516
29822
|
{ field: "subheading", value: config.subheading ?? "" },
|
|
29823
|
+
{ field: "hasHero", value: yesNo2(Boolean(config.hero)) },
|
|
28517
29824
|
{ field: "hasOverrides", value: yesNo2(Boolean(config.overrides)) },
|
|
28518
29825
|
{ field: "hasFooter", value: yesNo2(Boolean(config.footer)) },
|
|
28519
29826
|
{ field: "looseProductOrder", value: (config.looseProductOrder ?? []).length }
|
|
@@ -28528,10 +29835,11 @@ function printStorefront(config) {
|
|
|
28528
29835
|
layout: section.layout,
|
|
28529
29836
|
mode: section.mode,
|
|
28530
29837
|
products: section.mode === "manual" ? (section.productIds ?? []).length : "",
|
|
29838
|
+
links: section.mode === "manual" ? (section.items ?? []).filter((item) => item.type === "link").length : "",
|
|
28531
29839
|
rule: section.rule ? `${section.rule.type}${section.rule.productType ? `:${section.rule.productType}` : ""}${section.rule.tag ? `:${section.rule.tag}` : ""}` : "",
|
|
28532
29840
|
sort: section.sort ?? ""
|
|
28533
29841
|
})),
|
|
28534
|
-
["id", "title", "layout", "mode", "products", "rule", "sort"]
|
|
29842
|
+
["id", "title", "layout", "mode", "products", "links", "rule", "sort"]
|
|
28535
29843
|
);
|
|
28536
29844
|
process.stdout.write(
|
|
28537
29845
|
"\nFull config to edit and send back: `zanfia --json site storefront get`.\n"
|
|
@@ -28834,7 +30142,210 @@ Next page: --cursor ${page.nextCursor}
|
|
|
28834
30142
|
);
|
|
28835
30143
|
}
|
|
28836
30144
|
|
|
30145
|
+
// src/commands/social/channels.ts
|
|
30146
|
+
var clientOf = (command) => {
|
|
30147
|
+
const flags = getGlobalFlags(command);
|
|
30148
|
+
return { flags, client: createApiClient({ apiKey: flags.apiKey, verbose: flags.verbose }) };
|
|
30149
|
+
};
|
|
30150
|
+
var parseCount = (value, flag) => {
|
|
30151
|
+
if (value === void 0) return void 0;
|
|
30152
|
+
const parsed = Number.parseInt(value, 10);
|
|
30153
|
+
if (!Number.isInteger(parsed) || parsed < 1 || parsed > 50) {
|
|
30154
|
+
throw new Error(`${flag} must be an integer between 1 and 50.`);
|
|
30155
|
+
}
|
|
30156
|
+
return parsed;
|
|
30157
|
+
};
|
|
30158
|
+
var count = (value) => value === null || value === void 0 ? "-" : value.toLocaleString("en-US");
|
|
30159
|
+
var watchTime = (minutes2) => `${Math.floor(minutes2 / 60)}h ${String(Math.round(minutes2 % 60)).padStart(2, "0")}m`;
|
|
30160
|
+
var duration = (seconds) => `${Math.floor(seconds / 60)}:${String(Math.round(seconds % 60)).padStart(2, "0")}`;
|
|
30161
|
+
var videoRows = (videos) => videos.map((v) => ({
|
|
30162
|
+
videoId: v.videoId,
|
|
30163
|
+
when: (v.liveStartedAt ?? v.publishedAt).slice(0, 16).replace("T", " "),
|
|
30164
|
+
live: v.liveStartedAt ? "live" : "",
|
|
30165
|
+
views: count(v.viewCount),
|
|
30166
|
+
likes: count(v.likeCount),
|
|
30167
|
+
comments: count(v.commentCount),
|
|
30168
|
+
title: v.title.length > 50 ? `${v.title.slice(0, 47)}\u2026` : v.title
|
|
30169
|
+
}));
|
|
30170
|
+
var VIDEO_COLUMNS = ["videoId", "when", "live", "views", "likes", "comments", "title"];
|
|
30171
|
+
var write = (line) => {
|
|
30172
|
+
process.stdout.write(`${line}
|
|
30173
|
+
`);
|
|
30174
|
+
};
|
|
30175
|
+
function registerSocialChannelCommands(social) {
|
|
30176
|
+
const youtube = social.command("youtube").description("Connected YouTube channel: stats, analytics, videos, transcripts");
|
|
30177
|
+
youtube.command("stats").description("Channel counters + the 12 latest uploads (lives included)").action(async (_, command) => {
|
|
30178
|
+
const { flags, client } = clientOf(command);
|
|
30179
|
+
const response = await client.getYouTubeChannel();
|
|
30180
|
+
emit(flags, response, () => {
|
|
30181
|
+
const { channel } = response;
|
|
30182
|
+
write(`${channel.title ?? channel.channelId}`);
|
|
30183
|
+
write(` Subscribers: ${count(channel.subscriberCount)}`);
|
|
30184
|
+
write(` Views: ${count(channel.viewCount)}`);
|
|
30185
|
+
write(` Videos: ${count(channel.videoCount)}`);
|
|
30186
|
+
write("");
|
|
30187
|
+
printTable(videoRows(response.recentVideos), VIDEO_COLUMNS);
|
|
30188
|
+
});
|
|
30189
|
+
});
|
|
30190
|
+
youtube.command("analytics").description("Last 28 days of YouTube Analytics (needs the analytics grant)").action(async (_, command) => {
|
|
30191
|
+
const { flags, client } = clientOf(command);
|
|
30192
|
+
const response = await client.getYouTubeChannelAnalytics();
|
|
30193
|
+
emit(flags, response, () => {
|
|
30194
|
+
const { summary } = response;
|
|
30195
|
+
write(`YouTube Analytics ${summary.startDate} \u2192 ${summary.endDate}`);
|
|
30196
|
+
write(` Views: ${count(summary.views)}`);
|
|
30197
|
+
write(` Watch time: ${watchTime(summary.watchTimeMinutes)}`);
|
|
30198
|
+
write(` Average view duration: ${duration(summary.averageViewDurationSeconds)}`);
|
|
30199
|
+
write(` Subscribers: +${summary.subscribersGained} / -${summary.subscribersLost}`);
|
|
30200
|
+
});
|
|
30201
|
+
});
|
|
30202
|
+
youtube.command("videos").description("Uploads newest first (lives included) with public counters").option("--max <n>", "videos per page (default 24, max 50)").option("--page-token <token>", "nextPageToken of the previous page").action(async (options, command) => {
|
|
30203
|
+
const { flags, client } = clientOf(command);
|
|
30204
|
+
const maxResults = parseCount(options.max, "--max");
|
|
30205
|
+
const response = await client.listYouTubeVideos({
|
|
30206
|
+
...maxResults !== void 0 ? { maxResults } : {},
|
|
30207
|
+
...options.pageToken ? { pageToken: options.pageToken } : {}
|
|
30208
|
+
});
|
|
30209
|
+
emit(flags, response, () => {
|
|
30210
|
+
printTable(videoRows(response.videos), VIDEO_COLUMNS);
|
|
30211
|
+
if (response.nextPageToken) write(`
|
|
30212
|
+
Next page: --page-token ${response.nextPageToken}`);
|
|
30213
|
+
});
|
|
30214
|
+
});
|
|
30215
|
+
youtube.command("video").description("One video: views, likes, comments + watch time and average view duration").argument("<videoId>", "YouTube video id (watch?v=<id>) of a connected-channel video").action(async (videoId, _, command) => {
|
|
30216
|
+
const { flags, client } = clientOf(command);
|
|
30217
|
+
const response = await client.getYouTubeVideo(videoId);
|
|
30218
|
+
emit(flags, response, () => {
|
|
30219
|
+
const { video, analytics } = response;
|
|
30220
|
+
write(`${video.title} (${video.videoId})`);
|
|
30221
|
+
if (video.liveStartedAt) write(` Live started: ${video.liveStartedAt}`);
|
|
30222
|
+
else write(` Published: ${video.publishedAt}`);
|
|
30223
|
+
write(` Views (public counter): ${count(video.viewCount)}`);
|
|
30224
|
+
write(` Likes: ${count(video.likeCount)} Comments: ${count(video.commentCount)}`);
|
|
30225
|
+
if (analytics.status === "ok" /* Ok */) {
|
|
30226
|
+
write(` Analytics views: ${count(analytics.views)}`);
|
|
30227
|
+
write(` Watch time: ${watchTime(analytics.watchTimeMinutes)}`);
|
|
30228
|
+
write(` Average view duration: ${duration(analytics.averageViewDurationSeconds)}`);
|
|
30229
|
+
} else {
|
|
30230
|
+
write(` Analytics: ${analytics.message}`);
|
|
30231
|
+
}
|
|
30232
|
+
});
|
|
30233
|
+
});
|
|
30234
|
+
youtube.command("transcript").description("Plain-text transcript from the manually created captions of a video").argument("<videoId>", "YouTube video id of a connected-channel video").action(async (videoId, _, command) => {
|
|
30235
|
+
const { flags, client } = clientOf(command);
|
|
30236
|
+
const response = await client.getYouTubeVideoTranscript(videoId);
|
|
30237
|
+
emit(flags, response, () => {
|
|
30238
|
+
if (response.status === "ok" /* Ok */) {
|
|
30239
|
+
write(response.transcript);
|
|
30240
|
+
return;
|
|
30241
|
+
}
|
|
30242
|
+
write(
|
|
30243
|
+
response.reason === "no-manual-captions" /* NoManualCaptions */ ? "No downloadable captions: YouTube only hands out manually created ones. Upload the file to the media library and transcribe it there instead." : "This connection cannot read captions yet: reconnect the channel in the dashboard."
|
|
30244
|
+
);
|
|
30245
|
+
});
|
|
30246
|
+
});
|
|
30247
|
+
const instagram = social.command("instagram").description("Connected Instagram account: insights and posts");
|
|
30248
|
+
instagram.command("insights").description("Reach and views over the last 30 days").action(async (_, command) => {
|
|
30249
|
+
const { flags, client } = clientOf(command);
|
|
30250
|
+
const response = await client.getInstagramInsights();
|
|
30251
|
+
emit(flags, response, () => {
|
|
30252
|
+
if (!response.available) {
|
|
30253
|
+
write(`Insights unavailable: ${response.reason}`);
|
|
30254
|
+
return;
|
|
30255
|
+
}
|
|
30256
|
+
write("Instagram, last 30 days");
|
|
30257
|
+
write(` Reach: ${count(response.reach)}`);
|
|
30258
|
+
write(` Views: ${count(response.views)}`);
|
|
30259
|
+
});
|
|
30260
|
+
});
|
|
30261
|
+
instagram.command("media").description("Recent posts and reels newest first").option("--limit <n>", "items per page (default 24, max 50)").option("--after <cursor>", "nextCursor of the previous page").action(async (options, command) => {
|
|
30262
|
+
const { flags, client } = clientOf(command);
|
|
30263
|
+
const limit = parseCount(options.limit, "--limit");
|
|
30264
|
+
const response = await client.listInstagramMedia({
|
|
30265
|
+
...limit !== void 0 ? { limit } : {},
|
|
30266
|
+
...options.after ? { after: options.after } : {}
|
|
30267
|
+
});
|
|
30268
|
+
emit(flags, response, () => {
|
|
30269
|
+
printTable(
|
|
30270
|
+
response.items.map((item) => ({
|
|
30271
|
+
id: item.id,
|
|
30272
|
+
when: item.timestamp.slice(0, 16).replace("T", " "),
|
|
30273
|
+
type: item.mediaProductType ?? item.mediaType,
|
|
30274
|
+
likes: count(item.likeCount),
|
|
30275
|
+
comments: count(item.commentsCount),
|
|
30276
|
+
caption: (item.caption ?? "").replace(/\s+/g, " ").slice(0, 50)
|
|
30277
|
+
})),
|
|
30278
|
+
["id", "when", "type", "likes", "comments", "caption"]
|
|
30279
|
+
);
|
|
30280
|
+
if (response.nextCursor) write(`
|
|
30281
|
+
Next page: --after ${response.nextCursor}`);
|
|
30282
|
+
});
|
|
30283
|
+
});
|
|
30284
|
+
social.command("threads").description("Connected Threads profile").command("posts").description("Recent Threads posts newest first").action(async (_, command) => {
|
|
30285
|
+
const { flags, client } = clientOf(command);
|
|
30286
|
+
const response = await client.listThreadsPosts();
|
|
30287
|
+
emit(flags, response, () => {
|
|
30288
|
+
printTable(
|
|
30289
|
+
response.items.map((post) => ({
|
|
30290
|
+
id: post.id,
|
|
30291
|
+
when: (post.timestamp ?? "").slice(0, 16).replace("T", " "),
|
|
30292
|
+
text: (post.text ?? "").replace(/\s+/g, " ").slice(0, 60),
|
|
30293
|
+
permalink: post.permalink ?? ""
|
|
30294
|
+
})),
|
|
30295
|
+
["id", "when", "text", "permalink"]
|
|
30296
|
+
);
|
|
30297
|
+
});
|
|
30298
|
+
});
|
|
30299
|
+
social.command("snapshot").description("Latest weekly audience snapshot: followers per channel (read-only)").action(async (_, command) => {
|
|
30300
|
+
const { flags, client } = clientOf(command);
|
|
30301
|
+
const response = await client.getLatestSocialSnapshot();
|
|
30302
|
+
emit(flags, response, () => {
|
|
30303
|
+
const { snapshot } = response;
|
|
30304
|
+
if (!snapshot) {
|
|
30305
|
+
write("No audience snapshot in the last 90 days (capture one in the dashboard).");
|
|
30306
|
+
return;
|
|
30307
|
+
}
|
|
30308
|
+
write(`Audience snapshot ${snapshot.date}`);
|
|
30309
|
+
printTable(
|
|
30310
|
+
Object.entries(snapshot.counts).map(([channel, followers]) => ({
|
|
30311
|
+
channel,
|
|
30312
|
+
followers: count(followers),
|
|
30313
|
+
source: snapshot.source[channel] ?? ""
|
|
30314
|
+
})),
|
|
30315
|
+
["channel", "followers", "source"]
|
|
30316
|
+
);
|
|
30317
|
+
for (const [channel, reason] of Object.entries(snapshot.errors)) {
|
|
30318
|
+
write(` ${channel}: not captured (${reason})`);
|
|
30319
|
+
}
|
|
30320
|
+
});
|
|
30321
|
+
});
|
|
30322
|
+
}
|
|
30323
|
+
|
|
28837
30324
|
// src/commands/social/index.ts
|
|
30325
|
+
function updateInput(options) {
|
|
30326
|
+
const targets = splitList(options.targets);
|
|
30327
|
+
const mediaIds = splitList(options.media);
|
|
30328
|
+
if (options.media !== void 0 && options.clearMedia) {
|
|
30329
|
+
throw new Error("Pass either --media or --clear-media, not both.");
|
|
30330
|
+
}
|
|
30331
|
+
if (options.firstComment !== void 0 && options.clearFirstComment) {
|
|
30332
|
+
throw new Error("Pass either --first-comment or --clear-first-comment, not both.");
|
|
30333
|
+
}
|
|
30334
|
+
const input = {
|
|
30335
|
+
...options.text !== void 0 ? { text: options.text } : {},
|
|
30336
|
+
...targets.length > 0 ? { targets } : {},
|
|
30337
|
+
...mediaIds.length > 0 ? { mediaIds } : {},
|
|
30338
|
+
...options.clearMedia ? { mediaIds: [] } : {},
|
|
30339
|
+
...options.firstComment !== void 0 ? { firstComment: options.firstComment } : {},
|
|
30340
|
+
...options.clearFirstComment ? { firstComment: null } : {}
|
|
30341
|
+
};
|
|
30342
|
+
if (Object.keys(input).length === 0) {
|
|
30343
|
+
throw new Error(
|
|
30344
|
+
"Nothing to update: pass --text, --targets, --media, --clear-media, --first-comment or --clear-first-comment."
|
|
30345
|
+
);
|
|
30346
|
+
}
|
|
30347
|
+
return input;
|
|
30348
|
+
}
|
|
28838
30349
|
var splitList = (value) => (value ?? "").split(",").map((item) => item.trim()).filter(Boolean);
|
|
28839
30350
|
function composeInput(options) {
|
|
28840
30351
|
if (!options.text) {
|
|
@@ -28855,7 +30366,9 @@ function composeInput(options) {
|
|
|
28855
30366
|
};
|
|
28856
30367
|
}
|
|
28857
30368
|
function registerSocialCommands(program2) {
|
|
28858
|
-
const social = program2.command("social").description(
|
|
30369
|
+
const social = program2.command("social").description(
|
|
30370
|
+
"Social media \u2014 connected accounts, posts (drafts, scheduling, publishing) and the stats of the connected accounts (youtube / instagram / threads / snapshot)"
|
|
30371
|
+
);
|
|
28859
30372
|
social.command("accounts").description("List connected social accounts (the values to pass as --targets)").action(async (_, command) => {
|
|
28860
30373
|
const flags = getGlobalFlags(command);
|
|
28861
30374
|
const client = createApiClient({ apiKey: flags.apiKey, verbose: flags.verbose });
|
|
@@ -28867,13 +30380,14 @@ function registerSocialCommands(program2) {
|
|
|
28867
30380
|
network: a.network,
|
|
28868
30381
|
label: a.label,
|
|
28869
30382
|
schedule: a.supportsScheduling ? "yes" : "no",
|
|
28870
|
-
publishNow: a.supportsImmediatePublish ? "yes" : "no"
|
|
30383
|
+
publishNow: a.supportsImmediatePublish ? "yes" : "no",
|
|
30384
|
+
firstComment: a.firstCommentNeedsReconnect ? "reconnect" : ""
|
|
28871
30385
|
})),
|
|
28872
|
-
["target", "network", "label", "schedule", "publishNow"]
|
|
30386
|
+
["target", "network", "label", "schedule", "publishNow", "firstComment"]
|
|
28873
30387
|
);
|
|
28874
30388
|
});
|
|
28875
30389
|
});
|
|
28876
|
-
social.command("posts").description("List social posts (drafts, scheduled, published) newest first").option("--status <status>", "draft | scheduled | published | failed").option("--limit <n>", "max rows (default 25, max 100)").action(async (options, command) => {
|
|
30390
|
+
social.command("posts").description("List social posts (drafts, scheduled, published) newest first").option("--status <status>", "draft | scheduled | published | partially_failed | failed").option("--limit <n>", "max rows (default 25, max 100)").action(async (options, command) => {
|
|
28877
30391
|
const flags = getGlobalFlags(command);
|
|
28878
30392
|
const client = createApiClient({ apiKey: flags.apiKey, verbose: flags.verbose });
|
|
28879
30393
|
const response = await client.listSocialPosts({
|
|
@@ -28964,6 +30478,44 @@ function registerSocialCommands(program2) {
|
|
|
28964
30478
|
}
|
|
28965
30479
|
});
|
|
28966
30480
|
});
|
|
30481
|
+
social.command("update").description(
|
|
30482
|
+
"Edit a draft or scheduled post (a scheduled one stays at its time); unset options keep their value"
|
|
30483
|
+
).argument("<id>", "post id").option("--text <text>", "new post text").option("--targets <list>", "comma-separated targets (see `social accounts`)").option(
|
|
30484
|
+
"--media <ids>",
|
|
30485
|
+
"comma-separated Media Library ids, in slide order (replaces the media)"
|
|
30486
|
+
).option("--clear-media", "remove all media").option("--first-comment <text>", "new first comment").option("--clear-first-comment", "remove the first comment").action(async (id, options, command) => {
|
|
30487
|
+
const flags = getGlobalFlags(command);
|
|
30488
|
+
const client = createApiClient({ apiKey: flags.apiKey, verbose: flags.verbose });
|
|
30489
|
+
const post = await client.updateSocialPost(id, updateInput(options));
|
|
30490
|
+
emit(flags, post, () => {
|
|
30491
|
+
process.stdout.write(
|
|
30492
|
+
`Updated ${post.id} (${post.status}${post.scheduledAt ? `, ${post.scheduledAt}` : ""})
|
|
30493
|
+
`
|
|
30494
|
+
);
|
|
30495
|
+
});
|
|
30496
|
+
});
|
|
30497
|
+
social.command("unschedule").description("Move a scheduled post back to drafts (it will not be published at its time)").argument("<id>", "scheduled post id").action(async (id, _, command) => {
|
|
30498
|
+
const flags = getGlobalFlags(command);
|
|
30499
|
+
const client = createApiClient({ apiKey: flags.apiKey, verbose: flags.verbose });
|
|
30500
|
+
const post = await client.unscheduleSocialPost(id);
|
|
30501
|
+
emit(flags, post, () => {
|
|
30502
|
+
process.stdout.write(`Moved ${post.id} back to drafts
|
|
30503
|
+
`);
|
|
30504
|
+
});
|
|
30505
|
+
});
|
|
30506
|
+
social.command("delete").description(
|
|
30507
|
+
"Delete a post (draft, scheduled, or a published post's record: it stays live on the network). Requires --confirm."
|
|
30508
|
+
).argument("<id>", "post id").option("--confirm", "required \u2014 acknowledge the post is deleted for good").action(async (id, options, command) => {
|
|
30509
|
+
requireConfirm(options.confirm, `delete social post ${id}`);
|
|
30510
|
+
const flags = getGlobalFlags(command);
|
|
30511
|
+
const client = createApiClient({ apiKey: flags.apiKey, verbose: flags.verbose });
|
|
30512
|
+
const result = await client.deleteSocialPost(id);
|
|
30513
|
+
emit(flags, result, () => {
|
|
30514
|
+
process.stdout.write(`Deleted ${result.postId} (was ${result.previousStatus})
|
|
30515
|
+
`);
|
|
30516
|
+
});
|
|
30517
|
+
});
|
|
30518
|
+
registerSocialChannelCommands(social);
|
|
28967
30519
|
}
|
|
28968
30520
|
|
|
28969
30521
|
// src/commands/stats/index.ts
|
|
@@ -30415,6 +31967,16 @@ var GRAPH_NODE_KINDS = /* @__PURE__ */ new Set([
|
|
|
30415
31967
|
"delay"
|
|
30416
31968
|
]);
|
|
30417
31969
|
var GRAPH_CONDITION_BRANCHES = ["true", "false"];
|
|
31970
|
+
var GRAPH_GOAL_LABELS = {
|
|
31971
|
+
tagAdded: "gets a tag",
|
|
31972
|
+
anyProductPurchased: "buys any product",
|
|
31973
|
+
productPurchased: "buys a chosen product"
|
|
31974
|
+
};
|
|
31975
|
+
function describeGoal(goal) {
|
|
31976
|
+
const ids = goal.tagIds ?? goal.productIds ?? [];
|
|
31977
|
+
const label = GRAPH_GOAL_LABELS[goal.kind] ?? goal.kind;
|
|
31978
|
+
return `Goal: ${label}${ids.length > 0 ? ` (${ids.join(", ")})` : ""}, the run ends at once`;
|
|
31979
|
+
}
|
|
30418
31980
|
var EXCERPT_LENGTH = 120;
|
|
30419
31981
|
function isRecord3(value) {
|
|
30420
31982
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
@@ -30564,6 +32126,10 @@ function walkGraph(graph, templateNames) {
|
|
|
30564
32126
|
lines.push("Not reachable from any trigger (never runs):");
|
|
30565
32127
|
for (const node of orphans) lines.push(` ${describeNode(node, templateNames)}`);
|
|
30566
32128
|
}
|
|
32129
|
+
if (graph.goal) {
|
|
32130
|
+
if (lines.length > 0) lines.push("");
|
|
32131
|
+
lines.push(describeGoal(graph.goal));
|
|
32132
|
+
}
|
|
30567
32133
|
return lines;
|
|
30568
32134
|
}
|
|
30569
32135
|
function describeNodeRef(node) {
|
|
@@ -30663,8 +32229,13 @@ function describeEmail(node, templateNames) {
|
|
|
30663
32229
|
}
|
|
30664
32230
|
function describeCondition2(config) {
|
|
30665
32231
|
const rules = Array.isArray(config.rules) ? config.rules.filter(isRecord3) : [];
|
|
30666
|
-
|
|
30667
|
-
|
|
32232
|
+
const groups2 = Array.isArray(config.groups) ? config.groups.filter(isRecord3) : [];
|
|
32233
|
+
if (rules.length === 0 && groups2.length === 0) return "(no rules)";
|
|
32234
|
+
const match = optionalString(config.match);
|
|
32235
|
+
return [
|
|
32236
|
+
...rules.map(describeRule),
|
|
32237
|
+
...groups2.map((group) => `(${describeCondition2(group)})`)
|
|
32238
|
+
].join(match === "any" ? " OR " : " AND ");
|
|
30668
32239
|
}
|
|
30669
32240
|
function describeRule(rule) {
|
|
30670
32241
|
const field = optionalString(rule.field) ?? "?";
|
|
@@ -30742,7 +32313,7 @@ function printLogins(profiles) {
|
|
|
30742
32313
|
const row = toLoginRow(profile);
|
|
30743
32314
|
const name = row.workspaceName ?? "(unnamed)";
|
|
30744
32315
|
const slug = row.workspaceSlug ? ` [${row.workspaceSlug}]` : "";
|
|
30745
|
-
const member = row.kind === "member" ? " (community member)" : "";
|
|
32316
|
+
const member = row.kind === "member" ? " (community member)" : row.kind === "agent" ? " (community agent key)" : "";
|
|
30746
32317
|
const expires = row.expiresAt ? `, expires ${row.expiresAt.slice(0, 10)}` : "";
|
|
30747
32318
|
process.stdout.write(
|
|
30748
32319
|
`${row.active ? "*" : " "} ${name}${slug}${member} - ${row.workspaceId ?? row.key}${row.userEmail ? `, ${row.userEmail}` : ""}${expires}
|
|
@@ -30907,8 +32478,8 @@ function applyShortHelp(program2) {
|
|
|
30907
32478
|
|
|
30908
32479
|
// src/program.ts
|
|
30909
32480
|
function resolveCliVersion() {
|
|
30910
|
-
if ("0.
|
|
30911
|
-
return "0.
|
|
32481
|
+
if ("0.21.0".length > 0) {
|
|
32482
|
+
return "0.21.0";
|
|
30912
32483
|
}
|
|
30913
32484
|
try {
|
|
30914
32485
|
const raw = readFileSync28(new URL("../package.json", import.meta.url), "utf8");
|
|
@@ -30956,7 +32527,7 @@ Signing in \u2014 two roles, two consent pages:
|
|
|
30956
32527
|
registerOffersCommands(program2);
|
|
30957
32528
|
registerCheckoutSettingsCommands(program2);
|
|
30958
32529
|
registerIntegrationsCommands(program2);
|
|
30959
|
-
|
|
32530
|
+
registerSiteCommands2(program2);
|
|
30960
32531
|
registerEmailsCommands(program2);
|
|
30961
32532
|
registerOtoCommands(program2);
|
|
30962
32533
|
registerBumpsCommands(program2);
|
|
@@ -30986,6 +32557,7 @@ Signing in \u2014 two roles, two consent pages:
|
|
|
30986
32557
|
registerPagesCommands(program2);
|
|
30987
32558
|
registerSocialCommands(program2);
|
|
30988
32559
|
registerMediaCommands(program2);
|
|
32560
|
+
registerFilesCommands(program2);
|
|
30989
32561
|
applyGlobalOptions(program2);
|
|
30990
32562
|
applyShortHelp(program2);
|
|
30991
32563
|
return program2;
|