windmill-cli 1.401.0 → 1.402.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.
@@ -32,7 +32,7 @@ export const OpenAPI = {
32
32
  PASSWORD: undefined,
33
33
  TOKEN: getEnv("WM_TOKEN"),
34
34
  USERNAME: undefined,
35
- VERSION: '1.401.0',
35
+ VERSION: '1.402.0',
36
36
  WITH_CREDENTIALS: true,
37
37
  interceptors: {
38
38
  request: new Interceptors(),
@@ -4510,6 +4510,24 @@ export const getDbClock = () => {
4510
4510
  url: '/jobs/db_clock'
4511
4511
  });
4512
4512
  };
4513
+ /**
4514
+ * Count jobs by tag
4515
+ * @param data The data for the request.
4516
+ * @param data.horizonSecs Past Time horizon in seconds (when to start the count = now - horizon) (default is 3600)
4517
+ * @param data.workspaceId Specific workspace ID to filter results (optional)
4518
+ * @returns unknown Job counts by tag
4519
+ * @throws ApiError
4520
+ */
4521
+ export const countJobsByTag = (data = {}) => {
4522
+ return __request(OpenAPI, {
4523
+ method: 'GET',
4524
+ url: '/jobs/completed/count_by_tag',
4525
+ query: {
4526
+ horizon_secs: data.horizonSecs,
4527
+ workspace_id: data.workspaceId
4528
+ }
4529
+ });
4530
+ };
4513
4531
  /**
4514
4532
  * get job
4515
4533
  * @param data The data for the request.
package/esm/instance.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as dntShim from "./_dnt.shims.js";
2
- import { Select, path, Confirm, yamlStringify, yamlParse, Command, setClient, } from "./deps.js";
2
+ import { Select, path, Confirm, yamlStringify, yamlParse, Command, setClient, Table, } from "./deps.js";
3
3
  import * as wmill from "./gen/services.gen.js";
4
4
  import { Input, colors, log } from "./deps.js";
5
5
  import { loginInteractive } from "./login.js";
@@ -29,31 +29,35 @@ export async function allInstances() {
29
29
  return [];
30
30
  }
31
31
  }
32
- export async function addInstance() {
33
- let remote = await Input.prompt({
34
- message: "Enter the remote url of this instance",
35
- default: "https://app.windmill.dev/",
36
- });
37
- remote = new URL(remote).toString(); // add trailing slash in all cases!
38
- const defaultName = new URL(remote).hostname;
39
- const name = await Input.prompt({
40
- message: "Enter a name for this instance",
41
- default: defaultName,
42
- });
43
- const prefix = name.toLowerCase().replace(/[^a-z0-9]/g, "");
44
- let token = undefined;
32
+ export async function addInstance(opts, instanceName, remote, token) {
33
+ if (!remote) {
34
+ remote = await Input.prompt({
35
+ message: "Enter the remote url of this instance",
36
+ default: "https://my.windmill.dev/",
37
+ });
38
+ remote = new URL(remote).toString(); // add trailing slash in all cases!
39
+ }
40
+ if (!instanceName) {
41
+ const defaultName = new URL(remote).hostname.split(".")[0];
42
+ instanceName = await Input.prompt({
43
+ message: "Enter a name for this instance",
44
+ default: defaultName,
45
+ });
46
+ }
47
+ const prefix = instanceName.toLowerCase().replace(/[^a-z0-9]/g, "");
45
48
  while (!token) {
46
49
  token = await loginInteractive(remote);
47
50
  }
48
51
  await appendInstance({
49
- name,
52
+ name: instanceName,
50
53
  remote,
51
54
  token,
52
55
  prefix,
53
56
  });
54
- log.info(colors.green.underline(`Added instance ${name} with remote ${remote}!`));
57
+ log.info(colors.green.underline(`Added instance ${instanceName} with remote ${remote}!`));
58
+ await switchI({}, instanceName);
55
59
  return {
56
- name,
60
+ name: instanceName,
57
61
  remote,
58
62
  token,
59
63
  prefix,
@@ -101,31 +105,53 @@ export function compareInstanceObjects(fromObjects, toObjects, idProp, objectNam
101
105
  }
102
106
  return changes;
103
107
  }
104
- async function instancePull(opts) {
108
+ export async function pickInstance(opts, allowNew) {
105
109
  const instances = await allInstances();
106
- let instance;
107
- if (instances.length < 1) {
108
- instance = await addInstance();
110
+ if (opts.baseUrl && opts.token) {
111
+ log.info("Using instance fully defined by --base-url and --token");
112
+ return {
113
+ name: "custom",
114
+ remote: opts.baseUrl,
115
+ token: opts.token,
116
+ prefix: "custom",
117
+ };
109
118
  }
110
- else {
111
- const choice = (await Select.prompt({
112
- message: "Select an instance to pull from",
113
- options: [
114
- ...instances.map((i) => ({
115
- name: `${i.name} (${i.remote})`,
116
- value: i.name,
117
- })),
118
- { name: "Add new instance", value: "new" },
119
- ],
120
- }));
121
- if (choice === "new") {
122
- instance = await addInstance();
119
+ if (!allowNew && instances.length < 1) {
120
+ throw new Error("No instance found, please add one first");
121
+ }
122
+ const instanceName = await getActiveInstance(opts);
123
+ let instance = instances.find((i) => i.name === instanceName);
124
+ if (!instance) {
125
+ if (instances.length < 1) {
126
+ instance = await addInstance({}, undefined, undefined, undefined);
123
127
  }
124
128
  else {
125
- instance = instances.find((i) => i.name === choice);
129
+ const choice = (await Select.prompt({
130
+ message: "Select an instance",
131
+ options: [
132
+ ...instances.map((i) => ({
133
+ name: `${i.name} (${i.remote})`,
134
+ value: i.name,
135
+ })),
136
+ { name: "Add new instance", value: "new" },
137
+ ],
138
+ }));
139
+ if (choice === "new") {
140
+ instance = await addInstance({}, undefined, undefined, undefined);
141
+ }
142
+ else {
143
+ instance = instances.find((i) => i.name === choice);
144
+ }
126
145
  }
127
146
  }
147
+ else {
148
+ log.info(`Selected instance: ${instance.name}`);
149
+ }
128
150
  setClient(instance.token, instance.remote.slice(0, instance.remote.length - 1));
151
+ return instance;
152
+ }
153
+ async function instancePull(opts) {
154
+ const instance = await pickInstance(opts, true);
129
155
  log.info("Pulling instance-level changes");
130
156
  log.info(`remote (${instance.name}) -> local`);
131
157
  let uChanges = 0;
@@ -146,10 +172,13 @@ async function instancePull(opts) {
146
172
  }
147
173
  const totalChanges = uChanges + sChanges + cChanges + gChanges;
148
174
  if (totalChanges > 0) {
149
- const confirm = await Confirm.prompt({
150
- message: `Do you want to apply these ${totalChanges} instance-level changes?`,
151
- default: true,
152
- });
175
+ let confirm = true;
176
+ if (opts.yes !== true) {
177
+ confirm = await Confirm.prompt({
178
+ message: `Do you want to pull these ${totalChanges} instance-level changes?`,
179
+ default: true,
180
+ });
181
+ }
153
182
  if (confirm) {
154
183
  if (!opts.skipUsers && uChanges > 0) {
155
184
  await pullInstanceUsers();
@@ -227,29 +256,7 @@ async function instancePull(opts) {
227
256
  }
228
257
  async function instancePush(opts) {
229
258
  let instances = await allInstances();
230
- let instance;
231
- if (instances.length < 1) {
232
- instance = await addInstance();
233
- }
234
- else {
235
- const choice = (await Select.prompt({
236
- message: "Select an instance to push to",
237
- options: [
238
- ...instances.map((i) => ({
239
- name: `${i.name} (${i.remote})`,
240
- value: i.name,
241
- })),
242
- { name: "Add new instance", value: "new" },
243
- ],
244
- }));
245
- if (choice === "new") {
246
- instance = await addInstance();
247
- }
248
- else {
249
- instance = instances.find((i) => i.name === choice);
250
- }
251
- }
252
- setClient(instance.token, instance.remote.slice(0, instance.remote.length - 1));
259
+ const instance = await pickInstance(opts, true);
253
260
  log.info("Pushing instance-level changes");
254
261
  log.info(`remote (${instance.name}) <- local`);
255
262
  let uChanges = 0;
@@ -270,10 +277,13 @@ async function instancePush(opts) {
270
277
  }
271
278
  const totalChanges = uChanges + sChanges + cChanges + gChanges;
272
279
  if (totalChanges > 0) {
273
- const confirm = await Confirm.prompt({
274
- message: `Do you want to apply these ${totalChanges} instance-level changes?`,
275
- default: true,
276
- });
280
+ let confirm = true;
281
+ if (opts.yes !== true) {
282
+ confirm = await Confirm.prompt({
283
+ message: `Do you want to apply these ${totalChanges} instance-level changes?`,
284
+ default: true,
285
+ });
286
+ }
277
287
  if (confirm) {
278
288
  if (!opts.skipUsers && uChanges > 0) {
279
289
  await pushInstanceUsers();
@@ -365,11 +375,92 @@ async function instancePush(opts) {
365
375
  log.info(colors.green.underline.bold("All workspaces pushed"));
366
376
  }
367
377
  }
378
+ async function switchI(opts, instanceName) {
379
+ const all = await allInstances();
380
+ if (all.findIndex((x) => x.name === instanceName) === -1) {
381
+ log.info(colors.red.bold(`! This instance ${instanceName} does not exist locally.`));
382
+ log.info("available instances:");
383
+ for (const w of all) {
384
+ log.info(" - " + w.name);
385
+ }
386
+ return;
387
+ }
388
+ await dntShim.Deno.writeTextFile((await getRootStore()) + "/activeInstance", instanceName);
389
+ log.info(colors.green.underline(`Switched to instance ${instanceName}`));
390
+ }
391
+ export async function getActiveInstance(opts) {
392
+ if (opts.instance) {
393
+ return opts.instance;
394
+ }
395
+ try {
396
+ return await dntShim.Deno.readTextFile((await getRootStore()) + "/activeInstance");
397
+ }
398
+ catch {
399
+ return undefined;
400
+ }
401
+ }
402
+ async function whoami(opts) {
403
+ await pickInstance({}, false);
404
+ try {
405
+ const whoamiInfo = await wmill.globalWhoami();
406
+ log.info(colors.green.underline(`global whoami infos:`));
407
+ log.info(JSON.stringify(whoamiInfo, null, 2));
408
+ }
409
+ catch (error) {
410
+ log.error(colors.red(`Failed to retrieve whoami information: ${error.message}`));
411
+ }
412
+ }
368
413
  const command = new Command()
369
414
  .description("sync local with a remote instance or the opposite (push or pull)")
370
- .action(() => log.info("2 actions available, pull and push. Use -h to display help."))
415
+ .action(async () => {
416
+ log.info("4 actions available, add, remove, switch, pull and push. Use -h to display help.");
417
+ const activeInstance = await getActiveInstance({});
418
+ new Table()
419
+ .header(["name", "remote", "token"])
420
+ .padding(2)
421
+ .border(true)
422
+ .body((await allInstances()).map((x) => [
423
+ x.name === activeInstance ? colors.underline(x.name) : x.name,
424
+ x.remote,
425
+ x.token.substring(0, 7) + "***",
426
+ ]))
427
+ .render();
428
+ if (activeInstance) {
429
+ log.info(`Selected instance: ${activeInstance}`);
430
+ }
431
+ else {
432
+ log.info("No active instance selected");
433
+ }
434
+ log.info("Use 'wmill instance add' to add a new instance");
435
+ })
436
+ .command("add")
437
+ .description("Add a new instance")
438
+ .action(addInstance)
439
+ .arguments("[instance_name:string] [remote:string] [token:string]")
440
+ .command("remove")
441
+ .description("Remove an instance")
442
+ .complete("instance", async () => (await allInstances()).map((x) => x.name))
443
+ .arguments("<instance:string:instance>")
444
+ .action(async (instance) => {
445
+ const instances = await allInstances();
446
+ const choice = (await Select.prompt({
447
+ message: "Select an instance to remove",
448
+ options: instances.map((i) => ({
449
+ name: `${i.name} (${i.remote})`,
450
+ value: i.name,
451
+ })),
452
+ }));
453
+ await removeInstance(choice);
454
+ log.info(colors.green.underline(`Removed instance ${choice}`));
455
+ })
456
+ .command("switch")
457
+ .complete("instance", async () => (await allInstances()).map((x) => x.name))
458
+ .arguments("<instance:string:instance>")
459
+ .description("Switch the current instance")
460
+ .action(switchI)
371
461
  .command("pull")
372
462
  .description("Pull instance settings, users, configs, instance groups and overwrite local")
463
+ .option("--yes", "Pull without needing confirmation")
373
464
  .option("--skip-users", "Skip pulling users")
374
465
  .option("--skip-settings", "Skip pulling settings")
375
466
  .option("--skip-configs", "Skip pulling configs (worker groups and SMTP)")
@@ -378,11 +469,16 @@ const command = new Command()
378
469
  .action(instancePull)
379
470
  .command("push")
380
471
  .description("Push instance settings, users, configs, group and overwrite remote")
472
+ .option("--yes", "Push without needing confirmation")
381
473
  .option("--skip-users", "Skip pushing users")
382
474
  .option("--skip-settings", "Skip pushing settings")
383
475
  .option("--skip-configs", "Skip pushing configs (worker groups and SMTP)")
384
476
  .option("--skip-groups", "Skip pushing instance groups")
385
477
  .option("--include-workspaces", "Also push workspaces")
386
- .option("--base-url", "Base url to be passed to the instance settings instead of the local one")
387
- .action(instancePush);
478
+ .option("--instance", "Name of the instance to push to, override the active instance")
479
+ .option("--base-url", "If used with --token, will be used as the base url for the instance")
480
+ .action(instancePush)
481
+ .command("whoami")
482
+ .description("Display information about the currently logged-in user")
483
+ .action(whoami);
388
484
  export default command;
package/esm/main.js CHANGED
@@ -15,6 +15,7 @@ import folder from "./folder.js";
15
15
  import schedule from "./schedule.js";
16
16
  import sync from "./sync.js";
17
17
  import instance from "./instance.js";
18
+ import workerGroups from "./worker_groups.js";
18
19
  import dev from "./dev.js";
19
20
  import { fetchVersion } from "./context.js";
20
21
  import { OpenAPI } from "./gen/index.js";
@@ -23,6 +24,8 @@ import { NpmProvider } from "./upgrade.js";
23
24
  import { pull as hubPull } from "./hub.js";
24
25
  import { pull, push } from "./sync.js";
25
26
  import { add as workspaceAdd } from "./workspace.js";
27
+ import workers from "./workers.js";
28
+ import queues from "./queues.js";
26
29
  export { flow, app, script, workspace, resource, user, variable, hub, folder, schedule, sync, instance, dev, hubPull, pull, push, workspaceAdd, };
27
30
  // addEventListener("error", (event) => {
28
31
  // if (event.error) {
@@ -30,11 +33,11 @@ export { flow, app, script, workspace, resource, user, variable, hub, folder, sc
30
33
  // console.error(JSON.stringify(event.error, null, 4));
31
34
  // }
32
35
  // });
33
- export const VERSION = "1.401.0";
36
+ export const VERSION = "1.402.0";
34
37
  const command = new Command()
35
38
  .name("wmill")
36
39
  .action(() => log.info(`Welcome to Windmill CLI ${VERSION}. Use -h for help.`))
37
- .description("A simple CLI tool for windmill.")
40
+ .description("Windmill CLI")
38
41
  .globalOption("--workspace <workspace:string>", "Specify the target workspace. This overrides the default workspace.")
39
42
  .globalOption("--debug --verbose", "Show debug/verbose logs")
40
43
  .globalOption("--show-diffs", "Show diff informations when syncing (may show sensitive informations)")
@@ -69,6 +72,9 @@ const command = new Command()
69
72
  .command("dev", dev)
70
73
  .command("sync", sync)
71
74
  .command("instance", instance)
75
+ .command("worker-groups", workerGroups)
76
+ .command("workers", workers)
77
+ .command("queues", queues)
72
78
  .command("version", "Show version information")
73
79
  .action(async (opts) => {
74
80
  console.log("CLI build against " + VERSION);
package/esm/queues.js ADDED
@@ -0,0 +1,117 @@
1
+ import { Command, Table } from "./deps.js";
2
+ import { log } from "./deps.js";
3
+ import * as wmill from "./gen/services.gen.js";
4
+ import { pickInstance } from "./instance.js";
5
+ function createRow(tag, data) {
6
+ if (data[tag]) {
7
+ return;
8
+ }
9
+ else {
10
+ data[tag] = {
11
+ count: 0,
12
+ waiting: 0,
13
+ later: 0,
14
+ running: 0,
15
+ suspended: 0,
16
+ rps30s: "",
17
+ rps5min: "",
18
+ rps30min: "",
19
+ rps24h: "",
20
+ };
21
+ }
22
+ }
23
+ async function displayQueues(opts, workspace) {
24
+ const activeInstance = await pickInstance(opts, true);
25
+ if (activeInstance) {
26
+ try {
27
+ const queuedJobs = await wmill.listQueue({ workspace: workspace ?? 'admins', allWorkspaces: workspace === undefined, perPage: 100000 });
28
+ const jobCounts30s = await wmill.countJobsByTag({
29
+ horizonSecs: 30,
30
+ workspaceId: workspace,
31
+ });
32
+ const nowFromDb = new Date(await wmill.getDbClock());
33
+ const jobCounts5min = await wmill.countJobsByTag({
34
+ horizonSecs: 300,
35
+ workspaceId: workspace,
36
+ });
37
+ const jobCounts30min = await wmill.countJobsByTag({
38
+ horizonSecs: 1800,
39
+ workspaceId: workspace,
40
+ });
41
+ const jobCounts24h = await wmill.countJobsByTag({
42
+ horizonSecs: 86400,
43
+ workspaceId: workspace,
44
+ });
45
+ const data = {};
46
+ for (const job of queuedJobs) {
47
+ createRow(job.tag, data);
48
+ const scheduledFor = new Date(job.scheduled_for ?? "");
49
+ if (job.running) {
50
+ if (job.suspend) {
51
+ data[job.tag].suspended += 1;
52
+ }
53
+ else {
54
+ data[job.tag].running += 1;
55
+ }
56
+ }
57
+ else if (scheduledFor <= nowFromDb) {
58
+ data[job.tag].waiting += 1;
59
+ }
60
+ else {
61
+ data[job.tag].later += 1;
62
+ }
63
+ }
64
+ for (const count of jobCounts30s) {
65
+ const tag = count.tag;
66
+ createRow(tag, data);
67
+ data[tag].rps30s = (count.count / 30).toFixed(3);
68
+ }
69
+ for (const count of jobCounts5min) {
70
+ const tag = count.tag;
71
+ createRow(tag, data);
72
+ data[tag].rps5min = (count.count / 300).toFixed(3);
73
+ }
74
+ for (const count of jobCounts30min) {
75
+ const tag = count.tag;
76
+ createRow(tag, data);
77
+ data[tag].rps30min = (count.count / 1800).toFixed(3);
78
+ }
79
+ for (const count of jobCounts24h) {
80
+ const tag = count.tag;
81
+ createRow(tag, data);
82
+ data[tag].rps24h = (count.count / 86400).toFixed(3);
83
+ }
84
+ const table = new Table();
85
+ table.header([
86
+ "",
87
+ "Running",
88
+ "Waiting",
89
+ "Later",
90
+ "Suspended",
91
+ "RPS (30s)",
92
+ "RPS (5min)",
93
+ "RPS (30min)",
94
+ "RPS (24h)",
95
+ ]);
96
+ let body = [];
97
+ for (const tag in data) {
98
+ body.push([tag, data[tag].running, data[tag].waiting, data[tag].later, data[tag].suspended, data[tag].rps30s, data[tag].rps5min, data[tag].rps30min, data[tag].rps24h]);
99
+ }
100
+ table.body(body).render();
101
+ }
102
+ catch (error) {
103
+ log.error("Failed to fetch queue metrics:", error);
104
+ }
105
+ }
106
+ else {
107
+ log.info("No active instance found");
108
+ log.info("Use 'wmill instance add' to add a new instance");
109
+ }
110
+ }
111
+ const command = new Command()
112
+ .description("List all queues with their metrics")
113
+ .arguments("[workspace:string] the optional workspace to filter by (default to all workspaces)")
114
+ .option("--instance [instance]", "Name of the instance to push to, override the active instance")
115
+ .option("--base-url [baseUrl]", "If used with --token, will be used as the base url for the instance")
116
+ .action(displayQueues);
117
+ export default command;
package/esm/settings.js CHANGED
@@ -8,6 +8,7 @@ import { compareInstanceObjects } from "./instance.js";
8
8
  import { isSuperset } from "./types.js";
9
9
  import { deepEqual } from "./utils.js";
10
10
  import * as wmill from "./gen/services.gen.js";
11
+ import { removeWorkerPrefix } from "./worker_groups.js";
11
12
  export async function pushWorkspaceSettings(workspace, _path, settings, localSettings) {
12
13
  try {
13
14
  const remoteSettings = await wmill.getSettings({
@@ -271,7 +272,12 @@ export async function pushInstanceSettings(preview = false, baseUrl) {
271
272
  }
272
273
  }
273
274
  export async function pullInstanceConfigs(preview = false) {
274
- const remoteConfigs = await wmill.listConfigs();
275
+ const remoteConfigs = (await wmill.listConfigs()).map((x) => {
276
+ return {
277
+ ...x,
278
+ name: removeWorkerPrefix(x.name),
279
+ };
280
+ });
275
281
  if (preview) {
276
282
  let localConfigs = [];
277
283
  try {
@@ -287,7 +293,12 @@ export async function pullInstanceConfigs(preview = false) {
287
293
  }
288
294
  }
289
295
  export async function pushInstanceConfigs(preview = false) {
290
- const remoteConfigs = await wmill.listConfigs();
296
+ const remoteConfigs = (await wmill.listConfigs()).map((x) => {
297
+ return {
298
+ ...x,
299
+ name: removeWorkerPrefix(x.name),
300
+ };
301
+ });
291
302
  const localConfigs = (await dntShim.Deno.readTextFile("instance_configs.yaml")
292
303
  .then((raw) => yamlParse(raw))
293
304
  .catch(() => []));
@@ -303,7 +314,7 @@ export async function pushInstanceConfigs(preview = false) {
303
314
  }
304
315
  try {
305
316
  await wmill.updateConfig({
306
- name: config.name,
317
+ name: config.name.startsWith('worker__') ? config.name : `worker__${config.name}`,
307
318
  requestBody: config.config,
308
319
  });
309
320
  }
@@ -0,0 +1,96 @@
1
+ import { Command, Confirm, setClient, Table } from "./deps.js";
2
+ import { log } from "./deps.js";
3
+ import { allInstances, getActiveInstance, pickInstance } from "./instance.js";
4
+ import * as wmill from "./gen/services.gen.js";
5
+ import { pullInstanceConfigs, pushInstanceConfigs } from "./settings.js";
6
+ export async function getInstance(opts) {
7
+ const instances = await allInstances();
8
+ const instanceName = await getActiveInstance(opts);
9
+ const instance = instances.find((i) => i.name === instanceName);
10
+ if (instance) {
11
+ setClient(instance.token, instance.remote.slice(0, instance.remote.length - 1));
12
+ }
13
+ return instance;
14
+ }
15
+ export function removeWorkerPrefix(name) {
16
+ if (name.startsWith("worker__")) {
17
+ return name.substring(8);
18
+ }
19
+ return name;
20
+ }
21
+ export async function displayWorkerGroups(opts) {
22
+ log.info("2 actions available, pull and push.");
23
+ const activeInstance = await getActiveInstance({});
24
+ if (activeInstance) {
25
+ log.info("Active instance: " + activeInstance);
26
+ const instance = await getInstance({});
27
+ if (instance) {
28
+ const wGroups = await wmill.listWorkerGroups();
29
+ new Table()
30
+ .header(["name", "config"])
31
+ .padding(2)
32
+ .border(true)
33
+ .body(wGroups.map((x) => [removeWorkerPrefix(x.name), JSON.stringify(x.config, null, 2)]))
34
+ .render();
35
+ }
36
+ else {
37
+ log.error(`Instance ${activeInstance} not found`);
38
+ }
39
+ }
40
+ else {
41
+ log.info("No active instance found");
42
+ log.info("Use 'wmill instance add' to add a new instance");
43
+ }
44
+ }
45
+ async function pullWorkerGroups(opts) {
46
+ await pickInstance(opts, true);
47
+ const totalChanges = await pullInstanceConfigs(true) ?? 0;
48
+ if (totalChanges === 0) {
49
+ log.info("No changes to apply");
50
+ return;
51
+ }
52
+ let confirm = true;
53
+ if (opts.yes !== true) {
54
+ confirm = await Confirm.prompt({
55
+ message: `Do you want to pul these ${totalChanges} instance-level changes?`,
56
+ default: true,
57
+ });
58
+ }
59
+ if (confirm) {
60
+ await pullInstanceConfigs(false);
61
+ }
62
+ }
63
+ async function pushWorkerGroups(opts) {
64
+ await pickInstance(opts, true);
65
+ const totalChanges = await pushInstanceConfigs(true) ?? 0;
66
+ if (totalChanges === 0) {
67
+ log.info("No changes to apply");
68
+ return;
69
+ }
70
+ let confirm = true;
71
+ if (opts.yes !== true) {
72
+ confirm = await Confirm.prompt({
73
+ message: `Do you want to apply these ${totalChanges} instance-level changes?`,
74
+ default: true,
75
+ });
76
+ }
77
+ if (confirm) {
78
+ await pushInstanceConfigs(false);
79
+ }
80
+ }
81
+ const command = new Command()
82
+ .description("display worker groups, pull and push worker groups configs")
83
+ .action(displayWorkerGroups)
84
+ .command("pull")
85
+ .description("Pull worker groups (similar to `wmill instance pull --skip-users --skip-settings --skip-groups`)")
86
+ .option("--instance", "Name of the instance to push to, override the active instance")
87
+ .option("--base-url", "Base url to be passed to the instance settings instead of the local one")
88
+ .option("--yes", "Pull without needing confirmation")
89
+ .action(pullWorkerGroups)
90
+ .command("push")
91
+ .description("Push instance settings, users, configs, group and overwrite remote")
92
+ .option("--instance [instance]", "Name of the instance to push to, override the active instance")
93
+ .option("--base-url [baseUrl]", "If used with --token, will be used as the base url for the instance")
94
+ .option("--yes", "Push without needing confirmation")
95
+ .action(pushWorkerGroups);
96
+ export default command;