@koda-sl/baker-cli 0.233.0-dev.bfa5a91e4 → 0.234.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -58,7 +58,7 @@ import {
58
58
  ulid,
59
59
  validateCanvasDeep,
60
60
  ytDlpBlockSignal
61
- } from "./chunk-CMPAHYLB.js";
61
+ } from "./chunk-EKLAHWSF.js";
62
62
  import {
63
63
  csvOrJson,
64
64
  daysAgoIso,
@@ -5617,6 +5617,33 @@ var researchFetchResponseSchema = z17.object({
5617
5617
  provider: z17.literal("firecrawl")
5618
5618
  });
5619
5619
 
5620
+ // ../api/src/scheduledTaskModes.ts
5621
+ var TASK_MODES = {
5622
+ remind: {
5623
+ label: "Just reminds you",
5624
+ description: "Opens the Task on schedule and leaves it for you. Nothing runs."
5625
+ },
5626
+ recommend: {
5627
+ label: "Recommends",
5628
+ description: "Opens a Task with what it would change and why. Touches nothing itself."
5629
+ },
5630
+ propose: {
5631
+ label: "Prepares for review",
5632
+ description: "Does the work and leaves it for you to review, then publish."
5633
+ },
5634
+ publish: {
5635
+ label: "Publishes on its own",
5636
+ description: "Does the work and publishes it as soon as it finishes \u2014 nobody reviews it first."
5637
+ }
5638
+ };
5639
+ var TASK_MODE_IDS = Object.keys(TASK_MODES);
5640
+ function modeDescription(mode) {
5641
+ return TASK_MODES[mode].description;
5642
+ }
5643
+ function isTaskMode(value) {
5644
+ return TASK_MODE_IDS.some((id) => id === value);
5645
+ }
5646
+
5620
5647
  // ../api/src/studio.ts
5621
5648
  import { z as z18 } from "zod";
5622
5649
  var studioGenerationStatusSchema = z18.enum(["running", "completed", "partial", "failed"]);
@@ -31976,10 +32003,14 @@ async function draftList(json, chat) {
31976
32003
  // src/commands/ga4/config.ts
31977
32004
  registerSchema({
31978
32005
  command: "ga4.config",
31979
- description: "Read how a Google Analytics property is configured to measure: key events (conversions), custom dimensions and metrics, custom events, data streams and the data retention window. Start here before staging any change \u2014 every id an update or removal targets comes from this, and `canWrite` says whether this connection may change anything at all. Compact by default; pass --full for the raw Analytics objects.",
32006
+ description: "Read how a Google Analytics property is configured to measure: key events (conversions), custom dimensions and metrics, custom events, audiences, linked Google Ads accounts, data streams and the data retention window. Start here before staging any change \u2014 every id an update or removal targets comes from this, and `canWrite` says whether this connection may change anything at all. Compact by default; pass --full for the raw Analytics objects AND each website stream's enhanced measurement settings (the automatic events), which are the only way to see what is on before changing a toggle.",
31980
32007
  args: {
31981
32008
  "property-id": { type: "string", description: "GA4 property id (optional when one is connected)", required: false },
31982
- full: { type: "boolean", description: "Include the raw Analytics object for each entity", required: false }
32009
+ full: {
32010
+ type: "boolean",
32011
+ description: "Include the raw Analytics object for each entity, plus each website stream's enhanced measurement settings",
32012
+ required: false
32013
+ }
31983
32014
  }
31984
32015
  });
31985
32016
  function configHints(config) {
@@ -32012,11 +32043,15 @@ var configCommand = defineCommand116({
32012
32043
  Examples:
32013
32044
  baker ga4 config
32014
32045
  baker ga4 config --property-id 123456789
32015
- baker ga4 config --full`
32046
+ baker ga4 config --full # + the automatic events each website stream collects today`
32016
32047
  },
32017
32048
  args: {
32018
32049
  "property-id": { type: "string", description: "GA4 property id", required: false },
32019
- full: { type: "boolean", description: "Include raw Analytics objects", required: false }
32050
+ full: {
32051
+ type: "boolean",
32052
+ description: "Include raw Analytics objects and enhanced measurement settings",
32053
+ required: false
32054
+ }
32020
32055
  },
32021
32056
  run: async ({ args }) => {
32022
32057
  try {
@@ -32742,6 +32777,327 @@ Examples:
32742
32777
  })
32743
32778
  }
32744
32779
  });
32780
+ registerSchema({
32781
+ command: "ga4.audience",
32782
+ description: "Create an audience (a saved group of users, e.g. everyone who started a demo but never finished), rename one, or archive one. Read `baker ga4 config` first to see the audiences that already exist. IMPORTANT: everything that defines who is in an audience \u2014 the conditions, the membership window \u2014 is frozen the moment it is created and cannot be edited afterwards, so get it right in one go. An audience only reaches Google Ads for retargeting if the property is linked to a Google Ads account.",
32783
+ args: {
32784
+ json: { type: "string", description: createJsonDescription("audience"), required: false },
32785
+ file: { type: "string", description: "Path to a JSON file with the definition", required: false },
32786
+ "property-id": { type: "string", description: PROPERTY_ARG_DESCRIPTION, required: false }
32787
+ }
32788
+ });
32789
+ var audienceCommand = defineCommand120({
32790
+ meta: {
32791
+ name: "audience",
32792
+ description: `Stage audience changes (saved groups of users, for retargeting and analysis)
32793
+
32794
+ The definition is PERMANENT: conditions and membership window cannot be edited after creation.
32795
+ Fixing one means archiving it and building a replacement, which does not carry its users over.
32796
+
32797
+ Examples:
32798
+ # Everyone who visited but never submitted the demo form, kept for 90 days
32799
+ baker ga4 audience create --json '{
32800
+ "displayName":"Visited without demo",
32801
+ "description":"Reached the site, never submitted the demo form",
32802
+ "membershipDurationDays":90,
32803
+ "filterClauses":[
32804
+ {"clauseType":"INCLUDE","simpleFilter":{"scope":"AUDIENCE_FILTER_SCOPE_ACROSS_ALL_SESSIONS",
32805
+ "filterExpression":{"eventFilter":{"eventName":"page_view"}}}},
32806
+ {"clauseType":"EXCLUDE","simpleFilter":{"scope":"AUDIENCE_FILTER_SCOPE_ACROSS_ALL_SESSIONS",
32807
+ "filterExpression":{"eventFilter":{"eventName":"demo_form_submitted"}}}}
32808
+ ]}'
32809
+
32810
+ # Everyone who landed on a pricing page
32811
+ baker ga4 audience create --json '{"displayName":"Saw pricing","description":"Viewed a pricing page",
32812
+ "membershipDurationDays":60,
32813
+ "filterClauses":[{"clauseType":"INCLUDE","simpleFilter":{"scope":"AUDIENCE_FILTER_SCOPE_ACROSS_ALL_SESSIONS",
32814
+ "filterExpression":{"dimensionOrMetricFilter":{"fieldName":"pagePath",
32815
+ "stringFilter":{"matchType":"CONTAINS","value":"/pricing"}}}}}]}'
32816
+
32817
+ baker ga4 audience update 8813 --json '{"displayName":"Visited without demo (90d)"}' \u2014 name/description only
32818
+ baker ga4 audience archive 8813`
32819
+ },
32820
+ subCommands: {
32821
+ create: defineCommand120({
32822
+ meta: { name: "create", description: createJsonDescription("audience") },
32823
+ args: {
32824
+ json: { type: "string", description: createJsonDescription("audience"), required: false },
32825
+ file: { type: "string", description: "Path to a JSON file", required: false },
32826
+ "property-id": propertyArg
32827
+ },
32828
+ run: async ({ args }) => {
32829
+ const payloads = loadJsonPayloads(args);
32830
+ await stageOps(
32831
+ payloads.map((payload) => ({
32832
+ kind: "ga4.audience.create",
32833
+ payload,
32834
+ ...withProperty(args)
32835
+ }))
32836
+ );
32837
+ }
32838
+ }),
32839
+ update: defineCommand120({
32840
+ meta: {
32841
+ name: "update",
32842
+ description: "Stage a new name or description (pass the audience id). Who is in it cannot be changed."
32843
+ },
32844
+ args: {
32845
+ json: { type: "string", description: 'Inline JSON, e.g. {"displayName":"\u2026"}', required: false },
32846
+ file: { type: "string", description: "Path to a JSON file", required: false },
32847
+ "property-id": propertyArg
32848
+ },
32849
+ run: async ({ args }) => {
32850
+ const target = requireTarget4(args, "audience id");
32851
+ const payload = loadJsonArg(args);
32852
+ await stageOp3({
32853
+ kind: "ga4.audience.update",
32854
+ target,
32855
+ payload,
32856
+ ...withProperty(args)
32857
+ });
32858
+ }
32859
+ }),
32860
+ archive: defineCommand120({
32861
+ meta: {
32862
+ name: "archive",
32863
+ description: "Stage retiring an audience. Cannot be undone \u2014 the users it gathered do not come back."
32864
+ },
32865
+ args: { "property-id": propertyArg },
32866
+ run: async ({ args }) => {
32867
+ await stageOp3({
32868
+ kind: "ga4.audience.archive",
32869
+ target: requireTarget4(args, "audience id"),
32870
+ ...withProperty(args)
32871
+ });
32872
+ }
32873
+ })
32874
+ }
32875
+ });
32876
+ registerSchema({
32877
+ command: "ga4.property",
32878
+ description: "Set the property's name or industry category. The industry decides which businesses Google benchmarks this property against, so a property left on OTHER is being compared to nothing useful. Time zone and currency are deliberately not changeable here \u2014 both re-bucket every future day without reprocessing history, so they belong to a person who knows the reporting calendar.",
32879
+ args: {
32880
+ name: { type: "string", description: "New property display name", required: false },
32881
+ industry: {
32882
+ type: "string",
32883
+ description: "Industry category, e.g. TECHNOLOGY, FINANCE, HEALTHCARE, REAL_ESTATE, JOBS_AND_EDUCATION",
32884
+ required: false
32885
+ },
32886
+ "property-id": { type: "string", description: PROPERTY_ARG_DESCRIPTION, required: false }
32887
+ }
32888
+ });
32889
+ var propertyCommand = defineCommand120({
32890
+ meta: {
32891
+ name: "property",
32892
+ description: `Stage a change to the property's own settings
32893
+
32894
+ Examples:
32895
+ baker ga4 property set --industry TECHNOLOGY
32896
+ baker ga4 property set --name "Acme \u2014 Web" --industry REAL_ESTATE`
32897
+ },
32898
+ subCommands: {
32899
+ set: defineCommand120({
32900
+ meta: { name: "set", description: "Stage the property name and/or industry category" },
32901
+ args: {
32902
+ name: { type: "string", description: "New property display name", required: false },
32903
+ industry: { type: "string", description: "Industry category, e.g. TECHNOLOGY", required: false },
32904
+ "property-id": propertyArg
32905
+ },
32906
+ run: async ({ args }) => {
32907
+ const payload = {};
32908
+ if (typeof args.name === "string" && args.name.length > 0) {
32909
+ payload.displayName = args.name;
32910
+ }
32911
+ if (typeof args.industry === "string" && args.industry.length > 0) {
32912
+ payload.industryCategory = args.industry.trim().toUpperCase();
32913
+ }
32914
+ if (Object.keys(payload).length === 0) {
32915
+ failValidation3("pass --name, --industry, or both");
32916
+ }
32917
+ await stageOp3({ kind: "ga4.property.update", payload, ...withProperty(args) });
32918
+ }
32919
+ })
32920
+ }
32921
+ });
32922
+ registerSchema({
32923
+ command: "ga4.adsLink",
32924
+ description: "Link this Analytics property to a Google Ads account, or unlink it. This is what lets Google Ads import the property's key events for bidding and makes audiences built here targetable \u2014 `baker ga4 audit` reports the gap as NO_ADS_LINK. It reaches outside Analytics and moves the client's data between two products, so agree it with the user before staging it.",
32925
+ args: {
32926
+ "customer-id": {
32927
+ type: "string",
32928
+ description: "Google Ads customer id, with or without dashes (123-456-7890)",
32929
+ required: false
32930
+ },
32931
+ "ads-personalization": {
32932
+ type: "string",
32933
+ description: "true|false \u2014 may this property's audiences be used for ads personalisation",
32934
+ required: false
32935
+ },
32936
+ "property-id": { type: "string", description: PROPERTY_ARG_DESCRIPTION, required: false }
32937
+ }
32938
+ });
32939
+ var adsLinkCommand = defineCommand120({
32940
+ meta: {
32941
+ name: "ads-link",
32942
+ description: `Stage linking this property to a Google Ads account
32943
+
32944
+ Without this link, Google Ads imports no key events from the property and audiences built
32945
+ here cannot be retargeted. It moves the client's data between two products \u2014 agree it first.
32946
+
32947
+ Examples:
32948
+ baker ga4 ads-link create --customer-id 123-456-7890
32949
+ baker ga4 ads-link create --customer-id 1234567890 --ads-personalization true
32950
+ baker ga4 ads-link update L1234 --ads-personalization false
32951
+ baker ga4 ads-link delete L1234`
32952
+ },
32953
+ subCommands: {
32954
+ create: defineCommand120({
32955
+ meta: { name: "create", description: "Stage linking the property to a Google Ads account" },
32956
+ args: {
32957
+ "customer-id": { type: "string", description: "Google Ads customer id (123-456-7890)", required: false },
32958
+ "ads-personalization": { type: "string", description: "true|false", required: false },
32959
+ "property-id": propertyArg
32960
+ },
32961
+ run: async ({ args }) => {
32962
+ const customerId = args["customer-id"];
32963
+ if (typeof customerId !== "string" || customerId.length === 0) {
32964
+ failValidation3("pass --customer-id, the Google Ads account to link (123-456-7890)");
32965
+ }
32966
+ const payload = { customerId };
32967
+ if (typeof args["ads-personalization"] === "string") {
32968
+ payload.adsPersonalizationEnabled = booleanArg(args["ads-personalization"], "ads-personalization");
32969
+ }
32970
+ await stageOp3({ kind: "ga4.googleAdsLink.create", payload, ...withProperty(args) });
32971
+ }
32972
+ }),
32973
+ update: defineCommand120({
32974
+ meta: {
32975
+ name: "update",
32976
+ description: "Stage the ads-personalisation setting on an existing link (pass its id)"
32977
+ },
32978
+ args: {
32979
+ "ads-personalization": { type: "string", description: "true|false", required: false },
32980
+ "property-id": propertyArg
32981
+ },
32982
+ run: async ({ args }) => {
32983
+ const target = requireTarget4(args, "Google Ads link id");
32984
+ if (typeof args["ads-personalization"] !== "string") {
32985
+ failValidation3("pass --ads-personalization true|false \u2014 it is the only thing a link update can change");
32986
+ }
32987
+ await stageOp3({
32988
+ kind: "ga4.googleAdsLink.update",
32989
+ target,
32990
+ payload: { adsPersonalizationEnabled: booleanArg(args["ads-personalization"], "ads-personalization") },
32991
+ ...withProperty(args)
32992
+ });
32993
+ }
32994
+ }),
32995
+ delete: defineCommand120({
32996
+ meta: {
32997
+ name: "delete",
32998
+ description: "Stage unlinking. Stops conversion imports and strands every audience on this property."
32999
+ },
33000
+ args: { "property-id": propertyArg },
33001
+ run: async ({ args }) => {
33002
+ await stageOp3({
33003
+ kind: "ga4.googleAdsLink.delete",
33004
+ target: requireTarget4(args, "Google Ads link id"),
33005
+ ...withProperty(args)
33006
+ });
33007
+ }
33008
+ })
33009
+ }
33010
+ });
33011
+ registerSchema({
33012
+ command: "ga4.enhancedMeasurement",
33013
+ description: "Turn the automatic web-stream events on or off: scrolls, outbound clicks, site search, video engagement, file downloads, page changes (single-page apps), form interactions. These are collected by Google Analytics itself with no tag to add, so this is usually the cheapest measurement win available. Run `baker ga4 config --full` to see what is on today.",
33014
+ args: {
33015
+ scrolls: { type: "string", description: "true|false", required: false },
33016
+ "outbound-clicks": { type: "string", description: "true|false", required: false },
33017
+ "site-search": { type: "string", description: "true|false \u2014 needs --search-parameters", required: false },
33018
+ "search-parameters": {
33019
+ type: "string",
33020
+ description: "URL parameters carrying the search term, e.g. q,s,search,query,keyword",
33021
+ required: false
33022
+ },
33023
+ video: { type: "string", description: "true|false", required: false },
33024
+ "file-downloads": { type: "string", description: "true|false", required: false },
33025
+ "page-changes": { type: "string", description: "true|false \u2014 page views in a single-page app", required: false },
33026
+ "form-interactions": { type: "string", description: "true|false", required: false },
33027
+ enabled: { type: "string", description: "true|false \u2014 the master switch for the whole feature", required: false },
33028
+ "data-stream": {
33029
+ type: "string",
33030
+ description: "Only when the property has several website streams",
33031
+ required: false
33032
+ },
33033
+ "property-id": { type: "string", description: PROPERTY_ARG_DESCRIPTION, required: false }
33034
+ }
33035
+ });
33036
+ var MEASUREMENT_FLAGS = {
33037
+ enabled: "streamEnabled",
33038
+ scrolls: "scrollsEnabled",
33039
+ "outbound-clicks": "outboundClicksEnabled",
33040
+ "site-search": "siteSearchEnabled",
33041
+ video: "videoEngagementEnabled",
33042
+ "file-downloads": "fileDownloadsEnabled",
33043
+ "page-changes": "pageChangesEnabled",
33044
+ "form-interactions": "formInteractionsEnabled"
33045
+ };
33046
+ var enhancedMeasurementCommand = defineCommand120({
33047
+ meta: {
33048
+ name: "enhanced-measurement",
33049
+ description: `Stage the automatic events GA4 collects on a website stream
33050
+
33051
+ No tag to add \u2014 Google Analytics collects these itself. Usually the cheapest measurement win there is.
33052
+
33053
+ Examples:
33054
+ baker ga4 enhanced-measurement set --scrolls true --outbound-clicks true --file-downloads true
33055
+ baker ga4 enhanced-measurement set --site-search true --search-parameters "q,s,search,query,keyword"
33056
+ baker ga4 enhanced-measurement set --page-changes true # page views in a single-page app
33057
+ baker ga4 enhanced-measurement set --video false`
33058
+ },
33059
+ subCommands: {
33060
+ set: defineCommand120({
33061
+ meta: { name: "set", description: "Stage which automatic events the website stream collects" },
33062
+ args: {
33063
+ scrolls: { type: "string", description: "true|false", required: false },
33064
+ "outbound-clicks": { type: "string", description: "true|false", required: false },
33065
+ "site-search": { type: "string", description: "true|false", required: false },
33066
+ "search-parameters": { type: "string", description: "e.g. q,s,search,query,keyword", required: false },
33067
+ video: { type: "string", description: "true|false", required: false },
33068
+ "file-downloads": { type: "string", description: "true|false", required: false },
33069
+ "page-changes": { type: "string", description: "true|false", required: false },
33070
+ "form-interactions": { type: "string", description: "true|false", required: false },
33071
+ enabled: { type: "string", description: "true|false \u2014 master switch", required: false },
33072
+ "data-stream": { type: "string", description: "Stream id, when there are several", required: false },
33073
+ "property-id": propertyArg
33074
+ },
33075
+ run: async ({ args }) => {
33076
+ const payload = {};
33077
+ for (const [flag, field] of Object.entries(MEASUREMENT_FLAGS)) {
33078
+ const value = args[flag];
33079
+ if (typeof value === "string") {
33080
+ payload[field] = booleanArg(value, flag);
33081
+ }
33082
+ }
33083
+ if (typeof args["search-parameters"] === "string" && args["search-parameters"].length > 0) {
33084
+ payload.searchQueryParameter = args["search-parameters"];
33085
+ }
33086
+ if (Object.keys(payload).length === 0) {
33087
+ failValidation3(
33088
+ "pass at least one setting, e.g. --scrolls true --outbound-clicks true. Run `baker ga4 config --full` to see what is on today."
33089
+ );
33090
+ }
33091
+ await stageOp3({
33092
+ kind: "ga4.enhancedMeasurement.update",
33093
+ payload,
33094
+ ...typeof args["data-stream"] === "string" ? { dataStream: args["data-stream"] } : {},
33095
+ ...withProperty(args)
33096
+ });
33097
+ }
33098
+ })
33099
+ }
33100
+ });
32745
33101
 
32746
33102
  // src/commands/ga4/index.ts
32747
33103
  var ga4Command = defineCommand121({
@@ -32763,6 +33119,10 @@ Then stage changes (nothing is sent to Google Analytics until the chat completes
32763
33119
  baker ga4 key-event create --json '{"eventName":"generate_lead","countingMethod":"ONCE_PER_EVENT"}'
32764
33120
  baker ga4 custom-event create --json '{"destinationEvent":"quote_request","eventConditions":[...]}'
32765
33121
  baker ga4 custom-dimension create --json '{"parameterName":"plan_tier","displayName":"Plan tier","scope":"EVENT"}'
33122
+ baker ga4 audience create --json '{"displayName":"Visited without demo", \u2026}' \u2014 a group to retarget
33123
+ baker ga4 property set --industry TECHNOLOGY \u2014 benchmarking cohort
33124
+ baker ga4 ads-link create --customer-id 123-456-7890 \u2014 what makes audiences targetable
33125
+ baker ga4 enhanced-measurement set --scrolls true --outbound-clicks true \u2014 automatic events, no tag needed
32766
33126
  baker ga4 draft list \u2014 review everything staged
32767
33127
 
32768
33128
  An applied change takes effect on the property immediately \u2014 there is no version to publish afterwards
@@ -32780,6 +33140,10 @@ Full guide: __tooling__/docs/tools/baker/ga4.md`
32780
33140
  "custom-metric": customMetricCommand,
32781
33141
  "custom-event": customEventCommand,
32782
33142
  "data-retention": dataRetentionCommand,
33143
+ audience: audienceCommand,
33144
+ property: propertyCommand,
33145
+ "ads-link": adsLinkCommand,
33146
+ "enhanced-measurement": enhancedMeasurementCommand,
32783
33147
  draft: draftCommand3
32784
33148
  }
32785
33149
  });
@@ -43371,24 +43735,14 @@ function failIfModeAndSpawnAgent(args, mode) {
43371
43735
  failValidation4("--mode and --spawn-agent/--no-spawn-agent set the same thing. Use --mode on its own.");
43372
43736
  }
43373
43737
  }
43374
- var TASK_MODES = [
43375
- { id: "remind", does: "Opens the Task on schedule and leaves it. Nothing runs." },
43376
- { id: "recommend", does: "Opens a Task with what it would change and why. Touches nothing itself." },
43377
- { id: "propose", does: "Does the work and leaves it for the user to review, then publish." },
43378
- { id: "publish", does: "Does the work and publishes it as soon as it finishes \u2014 nobody reviews it first." }
43379
- ];
43380
- var TASK_MODE_IDS = TASK_MODES.map((mode) => mode.id);
43381
- var TASK_MODE_ARG_DESCRIPTION = `How far a run of this task goes on its own. ${TASK_MODES.map(
43382
- (mode) => `${mode.id}: ${mode.does}`
43738
+ var TASK_MODE_ARG_DESCRIPTION = `How far a run of this task goes on its own. ${TASK_MODE_IDS.map(
43739
+ (id) => `${id}: ${modeDescription(id)}`
43383
43740
  ).join(" ")} Only set publish when the user asked for it outright \u2014 those runs go live unreviewed and can spend money.`;
43384
- function isKnownTaskMode(raw) {
43385
- return typeof raw === "string" && TASK_MODE_IDS.some((id) => id === raw);
43386
- }
43387
43741
  function parseModeFlag(raw) {
43388
43742
  if (raw === void 0) {
43389
43743
  return void 0;
43390
43744
  }
43391
- if (!isKnownTaskMode(raw)) {
43745
+ if (!isTaskMode(raw)) {
43392
43746
  failValidation4(`--mode must be one of: ${TASK_MODE_IDS.join(", ")}.`);
43393
43747
  }
43394
43748
  return raw;
@@ -43452,7 +43806,7 @@ registerSchema({
43452
43806
  type: "string",
43453
43807
  description: `${TASK_MODE_ARG_DESCRIPTION} Defaults to propose, or to the template's own mode with --template.`,
43454
43808
  required: false,
43455
- enum: TASK_MODE_IDS
43809
+ enum: [...TASK_MODE_IDS]
43456
43810
  },
43457
43811
  "no-spawn-agent": {
43458
43812
  type: "boolean",
@@ -43857,7 +44211,7 @@ registerSchema({
43857
44211
  type: "string",
43858
44212
  description: TASK_MODE_ARG_DESCRIPTION,
43859
44213
  required: false,
43860
- enum: TASK_MODE_IDS
44214
+ enum: [...TASK_MODE_IDS]
43861
44215
  },
43862
44216
  "spawn-agent": {
43863
44217
  type: "string",