@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.
Files changed (2) hide show
  1. package/dist/index.js +1875 -303
  2. 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
- // ../../../Zanfia2/apps/cli/node_modules/commander/lib/error.js
36
+ // node_modules/commander/lib/error.js
37
37
  var require_error = __commonJS({
38
- "../../../Zanfia2/apps/cli/node_modules/commander/lib/error.js"(exports) {
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
- // ../../../Zanfia2/apps/cli/node_modules/commander/lib/argument.js
72
+ // node_modules/commander/lib/argument.js
73
73
  var require_argument = __commonJS({
74
- "../../../Zanfia2/apps/cli/node_modules/commander/lib/argument.js"(exports) {
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
- // ../../../Zanfia2/apps/cli/node_modules/commander/lib/help.js
200
+ // node_modules/commander/lib/help.js
201
201
  var require_help = __commonJS({
202
- "../../../Zanfia2/apps/cli/node_modules/commander/lib/help.js"(exports) {
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
- // ../../../Zanfia2/apps/cli/node_modules/commander/lib/option.js
615
+ // node_modules/commander/lib/option.js
616
616
  var require_option = __commonJS({
617
- "../../../Zanfia2/apps/cli/node_modules/commander/lib/option.js"(exports) {
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
- // ../../../Zanfia2/apps/cli/node_modules/commander/lib/suggestSimilar.js
888
+ // node_modules/commander/lib/suggestSimilar.js
889
889
  var require_suggestSimilar = __commonJS({
890
- "../../../Zanfia2/apps/cli/node_modules/commander/lib/suggestSimilar.js"(exports) {
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
- // ../../../Zanfia2/apps/cli/node_modules/commander/lib/command.js
969
+ // node_modules/commander/lib/command.js
970
970
  var require_command = __commonJS({
971
- "../../../Zanfia2/apps/cli/node_modules/commander/lib/command.js"(exports) {
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, write) => write(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 write;
2827
+ let write2;
2828
2828
  if (context.error) {
2829
- write = (arg) => this._outputConfiguration.writeErr(arg);
2829
+ write2 = (arg) => this._outputConfiguration.writeErr(arg);
2830
2830
  } else {
2831
- write = (arg) => this._outputConfiguration.writeOut(arg);
2831
+ write2 = (arg) => this._outputConfiguration.writeOut(arg);
2832
2832
  }
2833
- context.write = contextOptions.write || 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
- // ../../../Zanfia2/apps/cli/node_modules/commander/index.js
3013
+ // node_modules/commander/index.js
3014
3014
  var require_commander = __commonJS({
3015
- "../../../Zanfia2/apps/cli/node_modules/commander/index.js"(exports) {
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
- // ../../../Zanfia2/node_modules/requires-port/index.js
3036
+ // ../../node_modules/requires-port/index.js
3037
3037
  var require_requires_port = __commonJS({
3038
- "../../../Zanfia2/node_modules/requires-port/index.js"(exports, module) {
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
- // ../../../Zanfia2/node_modules/querystringify/index.js
3063
+ // ../../node_modules/querystringify/index.js
3064
3064
  var require_querystringify = __commonJS({
3065
- "../../../Zanfia2/node_modules/querystringify/index.js"(exports) {
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
- // ../../../Zanfia2/node_modules/url-parse/index.js
3115
+ // ../../node_modules/url-parse/index.js
3116
3116
  var require_url_parse = __commonJS({
3117
- "../../../Zanfia2/node_modules/url-parse/index.js"(exports, module) {
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 resolve2(relative, base) {
3225
- if (relative === "") return base;
3226
- var path2 = (base || "/").split("/").slice(0, -1).concat(relative.split("/")), i = path2.length, last = path2[i - 1], unshift = false, up = 0;
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 relative, extracted, parse2, instruction, index, key, instructions = rules.slice(), type = typeof location, url = this, i = 0;
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
- relative = !extracted.protocol && !extracted.slashes;
3258
- url.slashes = extracted.slashes || relative && location.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] || (relative && instruction[3] ? location[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 (relative && location.slashes && url.pathname.charAt(0) !== "/" && (url.pathname !== "" || location.pathname !== "")) {
3294
- url.pathname = resolve2(url.pathname, location.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
- // ../../../Zanfia2/node_modules/is-stream/index.js
3423
+ // ../../node_modules/is-stream/index.js
3424
3424
  var require_is_stream = __commonJS({
3425
- "../../../Zanfia2/node_modules/is-stream/index.js"(exports, module) {
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
- // ../../../Zanfia2/node_modules/lodash.throttle/index.js
3436
+ // ../../node_modules/lodash.throttle/index.js
3437
3437
  var require_lodash = __commonJS({
3438
- "../../../Zanfia2/node_modules/lodash.throttle/index.js"(exports, module) {
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
- // ../../../Zanfia2/node_modules/custom-error-instance/bin/factories.js
3585
+ // ../../node_modules/custom-error-instance/bin/factories.js
3586
3586
  var require_factories = __commonJS({
3587
- "../../../Zanfia2/node_modules/custom-error-instance/bin/factories.js"(exports) {
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
- // ../../../Zanfia2/node_modules/custom-error-instance/bin/error.js
3658
+ // ../../node_modules/custom-error-instance/bin/error.js
3659
3659
  var require_error2 = __commonJS({
3660
- "../../../Zanfia2/node_modules/custom-error-instance/bin/error.js"(exports, module) {
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
- // ../../../Zanfia2/node_modules/custom-error-instance/index.js
3769
+ // ../../node_modules/custom-error-instance/index.js
3770
3770
  var require_custom_error_instance = __commonJS({
3771
- "../../../Zanfia2/node_modules/custom-error-instance/index.js"(exports, module) {
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
- // ../../../Zanfia2/node_modules/lodash._basetostring/index.js
3777
+ // ../../node_modules/lodash._basetostring/index.js
3778
3778
  var require_lodash2 = __commonJS({
3779
- "../../../Zanfia2/node_modules/lodash._basetostring/index.js"(exports, module) {
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
- // ../../../Zanfia2/node_modules/lodash._stringtopath/index.js
3822
+ // ../../node_modules/lodash._stringtopath/index.js
3823
3823
  var require_lodash3 = __commonJS({
3824
- "../../../Zanfia2/node_modules/lodash._stringtopath/index.js"(exports, module) {
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
- // ../../../Zanfia2/node_modules/lodash._baseiteratee/index.js
4067
+ // ../../node_modules/lodash._baseiteratee/index.js
4068
4068
  var require_lodash4 = __commonJS({
4069
- "../../../Zanfia2/node_modules/lodash._baseiteratee/index.js"(exports, module) {
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
- // ../../../Zanfia2/node_modules/lodash._createset/index.js
4838
+ // ../../node_modules/lodash._createset/index.js
4839
4839
  var require_lodash5 = __commonJS({
4840
- "../../../Zanfia2/node_modules/lodash._createset/index.js"(exports, module) {
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
- // ../../../Zanfia2/node_modules/lodash._root/index.js
4927
+ // ../../node_modules/lodash._root/index.js
4928
4928
  var require_lodash6 = __commonJS({
4929
- "../../../Zanfia2/node_modules/lodash._root/index.js"(exports, module) {
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
- // ../../../Zanfia2/node_modules/lodash._baseuniq/index.js
4949
+ // ../../node_modules/lodash._baseuniq/index.js
4950
4950
  var require_lodash7 = __commonJS({
4951
- "../../../Zanfia2/node_modules/lodash._baseuniq/index.js"(exports, module) {
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
- // ../../../Zanfia2/node_modules/combine-errors/node_modules/lodash.uniqby/index.js
5252
+ // ../../node_modules/lodash.uniqby/index.js
5253
5253
  var require_lodash8 = __commonJS({
5254
- "../../../Zanfia2/node_modules/combine-errors/node_modules/lodash.uniqby/index.js"(exports, module) {
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
- // ../../../Zanfia2/node_modules/combine-errors/index.js
5265
+ // ../../node_modules/combine-errors/index.js
5266
5266
  var require_combine_errors = __commonJS({
5267
- "../../../Zanfia2/node_modules/combine-errors/index.js"(exports, module) {
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
- // ../../../Zanfia2/node_modules/graceful-fs/polyfills.js
5301
+ // ../../node_modules/graceful-fs/polyfills.js
5302
5302
  var require_polyfills = __commonJS({
5303
- "../../../Zanfia2/node_modules/graceful-fs/polyfills.js"(exports, module) {
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
- // ../../../Zanfia2/node_modules/graceful-fs/legacy-streams.js
5590
+ // ../../node_modules/graceful-fs/legacy-streams.js
5591
5591
  var require_legacy_streams = __commonJS({
5592
- "../../../Zanfia2/node_modules/graceful-fs/legacy-streams.js"(exports, module) {
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
- // ../../../Zanfia2/node_modules/graceful-fs/clone.js
5687
+ // ../../node_modules/graceful-fs/clone.js
5688
5688
  var require_clone = __commonJS({
5689
- "../../../Zanfia2/node_modules/graceful-fs/clone.js"(exports, module) {
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
- // ../../../Zanfia2/node_modules/graceful-fs/graceful-fs.js
5710
+ // ../../node_modules/graceful-fs/graceful-fs.js
5711
5711
  var require_graceful_fs = __commonJS({
5712
- "../../../Zanfia2/node_modules/graceful-fs/graceful-fs.js"(exports, module) {
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 = readFile4;
5797
- function readFile4(path2, options, cb) {
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 = readdir;
5868
+ fs3.readdir = readdir2;
5869
5869
  var noReaddirOptionVersions = /^v[0-5]\./;
5870
- function readdir(path2, options, cb) {
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
- // ../../../Zanfia2/node_modules/proper-lockfile/node_modules/retry/lib/retry_operation.js
6081
+ // ../../node_modules/proper-lockfile/node_modules/retry/lib/retry_operation.js
6082
6082
  var require_retry_operation = __commonJS({
6083
- "../../../Zanfia2/node_modules/proper-lockfile/node_modules/retry/lib/retry_operation.js"(exports, module) {
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 count = (counts[message] || 0) + 1;
6202
- counts[message] = count;
6203
- if (count >= mainErrorCount) {
6201
+ var count2 = (counts[message] || 0) + 1;
6202
+ counts[message] = count2;
6203
+ if (count2 >= mainErrorCount) {
6204
6204
  mainError = error;
6205
- mainErrorCount = count;
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
- // ../../../Zanfia2/node_modules/proper-lockfile/node_modules/retry/lib/retry.js
6213
+ // ../../node_modules/proper-lockfile/node_modules/retry/lib/retry.js
6214
6214
  var require_retry = __commonJS({
6215
- "../../../Zanfia2/node_modules/proper-lockfile/node_modules/retry/lib/retry.js"(exports) {
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
- // ../../../Zanfia2/node_modules/proper-lockfile/node_modules/retry/index.js
6300
+ // ../../node_modules/proper-lockfile/node_modules/retry/index.js
6301
6301
  var require_retry2 = __commonJS({
6302
- "../../../Zanfia2/node_modules/proper-lockfile/node_modules/retry/index.js"(exports, module) {
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
- // ../../../Zanfia2/node_modules/proper-lockfile/node_modules/signal-exit/signals.js
6308
+ // ../../node_modules/proper-lockfile/node_modules/signal-exit/signals.js
6309
6309
  var require_signals = __commonJS({
6310
- "../../../Zanfia2/node_modules/proper-lockfile/node_modules/signal-exit/signals.js"(exports, module) {
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
- // ../../../Zanfia2/node_modules/proper-lockfile/node_modules/signal-exit/index.js
6346
+ // ../../node_modules/proper-lockfile/node_modules/signal-exit/index.js
6347
6347
  var require_signal_exit = __commonJS({
6348
- "../../../Zanfia2/node_modules/proper-lockfile/node_modules/signal-exit/index.js"(exports, module) {
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
- // ../../../Zanfia2/node_modules/proper-lockfile/lib/mtime-precision.js
6506
+ // ../../node_modules/proper-lockfile/lib/mtime-precision.js
6507
6507
  var require_mtime_precision = __commonJS({
6508
- "../../../Zanfia2/node_modules/proper-lockfile/lib/mtime-precision.js"(exports, module) {
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, stat4) => {
6514
+ return fs2.stat(file, (err, stat6) => {
6515
6515
  if (err) {
6516
6516
  return callback(err);
6517
6517
  }
6518
- callback(null, stat4.mtime, cachedPrecision);
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, stat4) => {
6526
+ fs2.stat(file, (err2, stat6) => {
6527
6527
  if (err2) {
6528
6528
  return callback(err2);
6529
6529
  }
6530
- const precision = stat4.mtime.getTime() % 1e3 === 0 ? "s" : "ms";
6530
+ const precision = stat6.mtime.getTime() % 1e3 === 0 ? "s" : "ms";
6531
6531
  Object.defineProperty(fs2, cacheSymbol, { value: precision });
6532
- callback(null, stat4.mtime, precision);
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
- // ../../../Zanfia2/node_modules/proper-lockfile/lib/lockfile.js
6548
+ // ../../node_modules/proper-lockfile/lib/lockfile.js
6549
6549
  var require_lockfile = __commonJS({
6550
- "../../../Zanfia2/node_modules/proper-lockfile/lib/lockfile.js"(exports, module) {
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, stat4) => {
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(stat4, options)) {
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(stat4, options) {
6606
- return stat4.mtime.getTime() < Date.now() - options.stale;
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, stat4) => {
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() === stat4.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, stat4) => {
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(stat4, options));
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
- // ../../../Zanfia2/node_modules/proper-lockfile/lib/adapter.js
6785
+ // ../../node_modules/proper-lockfile/lib/adapter.js
6786
6786
  var require_adapter = __commonJS({
6787
- "../../../Zanfia2/node_modules/proper-lockfile/lib/adapter.js"(exports, module) {
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((resolve2, reject) => {
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
- resolve2(result);
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
- // ../../../Zanfia2/node_modules/proper-lockfile/index.js
6850
+ // ../../node_modules/proper-lockfile/index.js
6851
6851
  var require_proper_lockfile = __commonJS({
6852
- "../../../Zanfia2/node_modules/proper-lockfile/index.js"(exports, module) {
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
- // ../../../Zanfia2/apps/cli/node_modules/commander/esm.mjs
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 === "member" ? "member" : "workspace",
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, "live rooms are not available in mock mode");
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 - use get-blog-post for the Markdown. The id source for every other blog tool."
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. Returns the full post. Requires the blog early access on the workspace (403 otherwise)."
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("--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(
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 - prefer unpublishing."
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 after scrolling, after the cookie decision, never again after a signup"
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 content, triggers per device, targeting, frequency and schedule. The id source for every other pop-up tool."
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 holding a signup form. Without `content.formId` a new standalone signup form named after the pop-up is created (visible in Forms). Defaults: desktop after 10 s on the page, phones after 40 % scroll as a bottom sheet, again after 7 days, all hosted pages except thank-you / download / member pages, hidden for subscribers. Activate it with set-popup-status once it has a title and a button text. Requires the pop-ups feature on the plan (403 otherwise)."
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 content, triggers, targeting, frequency, schedule."
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
- "<connectionId>",
11402
- "Meta connection id (Settings \u2192 Integrations \u2192 Meta)"
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 label = kind === "member" ? isReadOnlyMemberKey(claims) ? "read-only member " : "member " : "";
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((resolve2) => {
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", () => resolve2(false));
12504
+ child.on("error", () => resolve3(false));
11883
12505
  child.unref();
11884
- setTimeout(() => resolve2(true), 100);
12506
+ setTimeout(() => resolve3(true), 100);
11885
12507
  } catch {
11886
- resolve2(false);
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((resolve2, reject) => {
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
- resolve2();
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((resolve2, reject) => {
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
- resolve2(result);
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
- constructor(message, status, code) {
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}): ${extractErrorMessage(text, response.statusText)}`,
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((resolve2) => {
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
- resolve2(value);
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: readFile4 } = await import("fs/promises");
13435
+ const { readFile: readFile6 } = await import("fs/promises");
12809
13436
  let patch = {};
12810
13437
  if (options.jsonFile) {
12811
- const raw = await readFile4(options.jsonFile, "utf8");
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)").action(
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("Revoke a client's product access (NO refund; live subscriptions are refused)").argument("<productId>", "product id").requiredOption("--email <email>", "client email").option("--run-automations", "also run the product's access-end automation tasks").action(
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
- `Cancelled ${options.email}'s subscription to product ${productId} \u2014 access runs to the end of the paid period (check \`clients accesses\` for the date).
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("--email-prefix <prefix>", "email prefix search (case-insensitive)").option("--limit <n>", "page size (max 100)").option("--cursor <cursor>", "pagination cursor from a previous page").action(
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("--direct-messages <on|off>", "allow / block member \u2194 member direct messages").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("--inactive", "create the code deactivated (default: active)").action(
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 duration = options.forever ? "forever" : void 0;
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("--active", "activate the code").option("--inactive", "deactivate the code").action(
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
- return `${(amountOffCents / 100).toFixed(2)} ${currency ?? ""}`.trim();
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 language = parseLanguage(options.lang, "pl");
18246
- const client = createApiClient({ apiKey: flags.apiKey, verbose: flags.verbose });
18247
- const result = await client.sendEmailTemplateTest(key, language);
18248
- emit(flags, result, () => {
18249
- process.stdout.write(`Test queued \u2192 ${result.email}
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 = ["lead_magnet", "tripwire", "sell_product"];
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
- "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: lead_magnet, tripwire, sell_product (webinar is a coming-soon recipe, not creatable yet)."
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, () => printFunnelDetail(result.funnel));
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) requires 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."
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 (opt_in_page / sales_page / thank_you \u2014 checkout/oto/downsell steps have their own surfaces) and wire the step to it. Consumes AI credits and runs 1-3 minutes synchronously \u2014 call it once per step, never speculatively. Pass the same --template across a funnel\u2019s steps so the pages look like siblings."
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").action(
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(" | ")})`).requiredOption("--product <productId>", "the product the funnel sells or delivers").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("--event-mode <mode>", `webinar: ${EVENT_MODES.join(" | ")} (on_demand = autowebinar)`).option(
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: parseChoice3(templateId, FUNNEL_TEMPLATE_IDS, "<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
- "Live rooms (webinar rooms, incl. rolling autowebinar sessions) \u2014 list and attendance; rooms are managed in the dashboard"
21324
+ "Webinars (incl. rolling autowebinar sessions) \u2014 list and attendance; webinars are managed in the dashboard"
20154
21325
  );
20155
- liveRooms.command("list").description("List live rooms (latest start first)").action(async (_, command) => {
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 room's attendance per session; --session adds that session's viewers").argument("<roomId>", "live room id (from live-rooms list)").option("--session <iso>", "ISO start of one session to list its attendees").action(async (roomId, options, command) => {
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
- Room 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)" : ""}
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 stat3 } from "fs/promises";
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 stat2 } from "fs/promises";
21413
+ import { stat as stat4 } from "fs/promises";
20243
21414
 
20244
- // ../../../Zanfia2/node_modules/tus-js-client/lib.esm/error.js
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
- // ../../../Zanfia2/node_modules/tus-js-client/lib.esm/logger.js
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
- // ../../../Zanfia2/node_modules/tus-js-client/lib.esm/noopUrlStorage.js
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
- // ../../../Zanfia2/node_modules/tus-js-client/node_modules/js-base64/base64.mjs
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
- // ../../../Zanfia2/node_modules/tus-js-client/lib.esm/upload.js
21808
+ // ../../node_modules/tus-js-client/lib.esm/upload.js
20638
21809
  var import_url_parse = __toESM(require_url_parse());
20639
21810
 
20640
- // ../../../Zanfia2/node_modules/tus-js-client/lib.esm/uuid.js
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
- // ../../../Zanfia2/node_modules/tus-js-client/lib.esm/upload.js
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, resolve2, reject, _next, _throw, key, arg) {
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
- resolve2(value);
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(resolve2, reject) {
22076
+ return new Promise(function(resolve3, reject) {
20906
22077
  var gen = fn.apply(self2, args);
20907
22078
  function _next(value) {
20908
- asyncGeneratorStep(gen, resolve2, reject, _next, _throw, "next", value);
22079
+ asyncGeneratorStep(gen, resolve3, reject, _next, _throw, "next", value);
20909
22080
  }
20910
22081
  function _throw(err) {
20911
- asyncGeneratorStep(gen, resolve2, reject, _next, _throw, "throw", err);
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(resolve2, reject) {
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: resolve2,
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(resolve2) {
21773
- return setTimeout(resolve2, delay);
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
- // ../../../Zanfia2/node_modules/tus-js-client/lib.esm/node/fileReader.js
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
- // ../../../Zanfia2/node_modules/tus-js-client/lib.esm/node/sources/BufferSource.js
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
- // ../../../Zanfia2/node_modules/tus-js-client/lib.esm/node/sources/FileSource.js
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, resolve2, reject, _next, _throw, key, arg) {
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
- resolve2(value);
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(resolve2, reject) {
23425
+ return new Promise(function(resolve3, reject) {
22255
23426
  var gen = fn.apply(self2, args);
22256
23427
  function _next(value) {
22257
- asyncGeneratorStep2(gen, resolve2, reject, _next, _throw, "next", value);
23428
+ asyncGeneratorStep2(gen, resolve3, reject, _next, _throw, "next", value);
22258
23429
  }
22259
23430
  function _throw(err) {
22260
- asyncGeneratorStep2(gen, resolve2, reject, _next, _throw, "throw", err);
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
- // ../../../Zanfia2/node_modules/tus-js-client/lib.esm/node/sources/StreamSource.js
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, resolve2, reject, _next, _throw, key, arg) {
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
- resolve2(value);
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(resolve2, reject) {
23800
+ return new Promise(function(resolve3, reject) {
22630
23801
  var gen = fn.apply(self2, args);
22631
23802
  function _next(value) {
22632
- asyncGeneratorStep3(gen, resolve2, reject, _next, _throw, "next", value);
23803
+ asyncGeneratorStep3(gen, resolve3, reject, _next, _throw, "next", value);
22633
23804
  }
22634
23805
  function _throw(err) {
22635
- asyncGeneratorStep3(gen, resolve2, reject, _next, _throw, "throw", err);
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(resolve2, reject) {
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
- resolve2(chunk2);
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
- // ../../../Zanfia2/node_modules/tus-js-client/lib.esm/node/fileReader.js
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
- // ../../../Zanfia2/node_modules/tus-js-client/lib.esm/node/fileSignature.js
22840
- import { createHash as createHash3 } from "crypto";
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 = createHash3("md5").update(content).digest("hex");
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(resolve2, reject) {
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
- resolve2(ret2);
24031
+ resolve3(ret2);
22861
24032
  });
22862
24033
  });
22863
24034
  }
22864
24035
  return Promise.resolve(null);
22865
24036
  }
22866
24037
 
22867
- // ../../../Zanfia2/node_modules/tus-js-client/lib.esm/node/httpStack.js
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(resolve2, reject) {
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
- resolve2(new Response(res, responseText));
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
- // ../../../Zanfia2/node_modules/tus-js-client/lib.esm/node/urlStorage.js
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
- // ../../../Zanfia2/node_modules/tus-js-client/lib.esm/node/index.js
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 stat2(params.filePath);
24496
+ const { size } = await stat4(params.filePath);
23326
24497
  const stream = createReadStream2(params.filePath);
23327
- await new Promise((resolve2, reject) => {
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: () => resolve2()
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((resolve2) => setTimeout(resolve2, ms));
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 readFile(options.file);
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 stat3(options.file);
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(([stat4, value]) => ({
24227
- stat: stat4,
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
- quantityLimit: p.quantityLimit ?? ""
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 readFile2 } from "fs/promises";
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 readFile2(options.file, "utf8");
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, or its tracking tools)"
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
- pages.command("publish").description("Publish a page").argument("<id>", "page id").action(async (id, _, command) => {
25797
- const flags = getGlobalFlags(command);
25798
- const client = createApiClient({ apiKey: flags.apiKey, verbose: flags.verbose });
25799
- const result = await client.publishPage(id, true);
25800
- emit(flags, result, () => {
25801
- process.stdout.write(`Page ${result.pageId} is now ${result.status}
25802
- `);
25803
- });
25804
- });
25805
- pages.command("unpublish").description("Revert a page to draft").argument("<id>", "page id").action(async (id, _, command) => {
25806
- const flags = getGlobalFlags(command);
25807
- const client = createApiClient({ apiKey: flags.apiKey, verbose: flags.verbose });
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("--event-mode <mode>", `webinar: ${EVENT_MODES2.join(" | ")} (on_demand = autowebinar)`).option("--replay-media <mediaId>", "on-demand webinar: Media Library video the room plays").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 readFile3 } from "fs/promises";
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 readFile3(filePath);
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
- `Backfill done: ${result.added} added, ${result.alreadyMembers} already members, ${result.skipped} skipped (no auth user yet \u2014 the lazy backstop covers them).
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 duration = options.duration !== void 0 ? parseNonNegativeInt3(options.duration, "--duration") : void 0;
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: duration ?? base2.duration ?? 0,
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
- return `${condition.operator === "has-any" ? "has any of" : "has none of"} tags ${condition.tagIds.join(", ")}`;
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 registerSiteCommands(program2) {
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("The product storefront layout: sections, preset, theme mode, footer");
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("Social media posts \u2014 connected accounts, drafts, scheduling, publishing");
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
- if (rules.length === 0) return "(no rules)";
30667
- return rules.map(describeRule).join(" AND ");
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.13.1".length > 0) {
30911
- return "0.13.1";
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
- registerSiteCommands(program2);
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;