tsarr 2.1.0 → 2.3.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/index.js CHANGED
@@ -1,5 +1,4 @@
1
- #!/usr/bin/env bun
2
- // @bun
1
+ #!/usr/bin/env node
3
2
  var __defProp = Object.defineProperty;
4
3
  var __returnValue = (v) => v;
5
4
  function __exportSetter(name, newValue) {
@@ -88,7 +87,7 @@ var init_scule = __esm(() => {
88
87
  });
89
88
 
90
89
  // node_modules/citty/dist/index.mjs
91
- import { parseArgs as parseArgs$1 } from "util";
90
+ import { parseArgs as parseArgs$1 } from "node:util";
92
91
  function toArray(val) {
93
92
  if (Array.isArray(val))
94
93
  return val;
@@ -714,7 +713,7 @@ var separatorArrayExplode = (style) => {
714
713
  return "";
715
714
  }
716
715
  if (typeof value === "object") {
717
- throw new Error("Deeply-nested arrays/objects aren\u2019t supported. Provide your own `querySerializer()` to handle these.");
716
+ throw new Error("Deeply-nested arrays/objects aren’t supported. Provide your own `querySerializer()` to handle these.");
718
717
  }
719
718
  return `${name}=${allowReserved ? value : encodeURIComponent(value)}`;
720
719
  }, serializeObjectParam = ({
@@ -2794,6 +2793,10 @@ class RadarrClient {
2794
2793
  updateConfig(newConfig) {
2795
2794
  const updatedConfig = { ...this.clientConfig.config, ...newConfig };
2796
2795
  this.clientConfig = createServarrClient(updatedConfig);
2796
+ client.setConfig({
2797
+ baseUrl: this.clientConfig.getBaseUrl(),
2798
+ headers: this.clientConfig.getHeaders()
2799
+ });
2797
2800
  return this.clientConfig.config;
2798
2801
  }
2799
2802
  }
@@ -3203,8 +3206,8 @@ var init_core = __esm(() => {
3203
3206
  });
3204
3207
 
3205
3208
  // node_modules/consola/dist/shared/consola.DRwqZj3T.mjs
3206
- import { formatWithOptions } from "util";
3207
- import { sep } from "path";
3209
+ import { formatWithOptions } from "node:util";
3210
+ import { sep } from "node:path";
3208
3211
  function parseStack(stack, message) {
3209
3212
  const cwd = process.cwd() + sep;
3210
3213
  const lines = stack.split(`
@@ -3281,7 +3284,7 @@ var bracket = (x) => x ? `[${x}]` : "";
3281
3284
  var init_consola_DRwqZj3T = () => {};
3282
3285
 
3283
3286
  // node_modules/consola/dist/shared/consola.DXBYu-KD.mjs
3284
- import * as tty from "tty";
3287
+ import * as tty from "node:tty";
3285
3288
  function replaceClose(index, string, close, replace, head = string.slice(0, Math.max(0, index)) + replace, tail = string.slice(Math.max(0, index + close.length)), next = tail.indexOf(close)) {
3286
3289
  return head + (next < 0 ? tail : replaceClose(next, tail, close, replace));
3287
3290
  }
@@ -3421,68 +3424,68 @@ var init_consola_DXBYu_KD = __esm(() => {
3421
3424
  ].join("|");
3422
3425
  boxStylePresets = {
3423
3426
  solid: {
3424
- tl: "\u250C",
3425
- tr: "\u2510",
3426
- bl: "\u2514",
3427
- br: "\u2518",
3428
- h: "\u2500",
3429
- v: "\u2502"
3427
+ tl: "",
3428
+ tr: "",
3429
+ bl: "",
3430
+ br: "",
3431
+ h: "",
3432
+ v: ""
3430
3433
  },
3431
3434
  double: {
3432
- tl: "\u2554",
3433
- tr: "\u2557",
3434
- bl: "\u255A",
3435
- br: "\u255D",
3436
- h: "\u2550",
3437
- v: "\u2551"
3435
+ tl: "",
3436
+ tr: "",
3437
+ bl: "",
3438
+ br: "",
3439
+ h: "",
3440
+ v: ""
3438
3441
  },
3439
3442
  doubleSingle: {
3440
- tl: "\u2553",
3441
- tr: "\u2556",
3442
- bl: "\u2559",
3443
- br: "\u255C",
3444
- h: "\u2500",
3445
- v: "\u2551"
3443
+ tl: "",
3444
+ tr: "",
3445
+ bl: "",
3446
+ br: "",
3447
+ h: "",
3448
+ v: ""
3446
3449
  },
3447
3450
  doubleSingleRounded: {
3448
- tl: "\u256D",
3449
- tr: "\u256E",
3450
- bl: "\u2570",
3451
- br: "\u256F",
3452
- h: "\u2500",
3453
- v: "\u2551"
3451
+ tl: "",
3452
+ tr: "",
3453
+ bl: "",
3454
+ br: "",
3455
+ h: "",
3456
+ v: ""
3454
3457
  },
3455
3458
  singleThick: {
3456
- tl: "\u250F",
3457
- tr: "\u2513",
3458
- bl: "\u2517",
3459
- br: "\u251B",
3460
- h: "\u2501",
3461
- v: "\u2503"
3459
+ tl: "",
3460
+ tr: "",
3461
+ bl: "",
3462
+ br: "",
3463
+ h: "",
3464
+ v: ""
3462
3465
  },
3463
3466
  singleDouble: {
3464
- tl: "\u2552",
3465
- tr: "\u2555",
3466
- bl: "\u2558",
3467
- br: "\u255B",
3468
- h: "\u2550",
3469
- v: "\u2502"
3467
+ tl: "",
3468
+ tr: "",
3469
+ bl: "",
3470
+ br: "",
3471
+ h: "",
3472
+ v: ""
3470
3473
  },
3471
3474
  singleDoubleRounded: {
3472
- tl: "\u256D",
3473
- tr: "\u256E",
3474
- bl: "\u2570",
3475
- br: "\u256F",
3476
- h: "\u2550",
3477
- v: "\u2502"
3475
+ tl: "",
3476
+ tr: "",
3477
+ bl: "",
3478
+ br: "",
3479
+ h: "",
3480
+ v: ""
3478
3481
  },
3479
3482
  rounded: {
3480
- tl: "\u256D",
3481
- tr: "\u256E",
3482
- bl: "\u2570",
3483
- br: "\u256F",
3484
- h: "\u2500",
3485
- v: "\u2502"
3483
+ tl: "",
3484
+ tr: "",
3485
+ bl: "",
3486
+ br: "",
3487
+ h: "",
3488
+ v: ""
3486
3489
  }
3487
3490
  };
3488
3491
  defaultStyle = {
@@ -3502,9 +3505,9 @@ __export(exports_prompt, {
3502
3505
  prompt: () => prompt,
3503
3506
  kCancel: () => kCancel
3504
3507
  });
3505
- import g, { stdin, stdout } from "process";
3506
- import f from "readline";
3507
- import { WriteStream } from "tty";
3508
+ import g, { stdin, stdout } from "node:process";
3509
+ import f from "node:readline";
3510
+ import { WriteStream } from "node:tty";
3508
3511
  function getDefaultExportFromCjs(x) {
3509
3512
  return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, "default") ? x["default"] : x;
3510
3513
  }
@@ -4137,7 +4140,7 @@ var init_prompt = __esm(() => {
4137
4140
  eD = Object.keys(r.bgColor);
4138
4141
  [...tD, ...eD];
4139
4142
  iD = sD();
4140
- v = new Set(["\x1B", "\x9B"]);
4143
+ v = new Set(["\x1B", "›"]);
4141
4144
  y = `${rD}8;;`;
4142
4145
  aD = ["up", "down", "left", "right", "space", "enter", "cancel"];
4143
4146
  c = { actions: new Set(aD), aliases: new Map([["k", "up"], ["j", "down"], ["h", "left"], ["l", "right"], ["\x03", "cancel"], ["escape", "cancel"]]) };
@@ -4223,7 +4226,7 @@ var init_prompt = __esm(() => {
4223
4226
  if (this.state === "submit")
4224
4227
  return this.value;
4225
4228
  if (this.cursor >= this.value.length)
4226
- return `${this.value}\u2588`;
4229
+ return `${this.value}█`;
4227
4230
  const u = this.value.slice(0, this.cursor), [F, ...e$1] = this.value.slice(this.cursor);
4228
4231
  return `${u}${e.inverse(F)}${e$1.join("")}`;
4229
4232
  }
@@ -4237,23 +4240,23 @@ var init_prompt = __esm(() => {
4237
4240
  }
4238
4241
  };
4239
4242
  V = ce();
4240
- le = u("\u276F", ">");
4241
- L = u("\u25A0", "x");
4242
- W = u("\u25B2", "x");
4243
- C = u("\u2714", "\u221A");
4243
+ le = u("", ">");
4244
+ L = u("", "x");
4245
+ W = u("", "x");
4246
+ C = u("", "");
4244
4247
  o = u("");
4245
4248
  d = u("");
4246
- k = u("\u25CF", ">");
4247
- P = u("\u25CB", " ");
4248
- A = u("\u25FB", "[\u2022]");
4249
- T = u("\u25FC", "[+]");
4250
- F = u("\u25FB", "[ ]");
4249
+ k = u("", ">");
4250
+ P = u("", " ");
4251
+ A = u("", "[]");
4252
+ T = u("", "[+]");
4253
+ F = u("", "[ ]");
4251
4254
  `${e.gray(o)} `;
4252
4255
  kCancel = Symbol.for("cancel");
4253
4256
  });
4254
4257
 
4255
4258
  // node_modules/consola/dist/index.mjs
4256
- import g$1 from "process";
4259
+ import g$1 from "node:process";
4257
4260
  function b() {
4258
4261
  if (globalThis.process?.env)
4259
4262
  for (const e2 of f2) {
@@ -4477,16 +4480,16 @@ var init_dist2 = __esm(() => {
4477
4480
  };
4478
4481
  unicode = isUnicodeSupported();
4479
4482
  TYPE_ICONS = {
4480
- error: s("\u2716", "\xD7"),
4481
- fatal: s("\u2716", "\xD7"),
4482
- ready: s("\u2714", "\u221A"),
4483
- warn: s("\u26A0", "\u203C"),
4484
- info: s("\u2139", "i"),
4485
- success: s("\u2714", "\u221A"),
4486
- debug: s("\u2699", "D"),
4487
- trace: s("\u2192", "\u2192"),
4488
- fail: s("\u2716", "\xD7"),
4489
- start: s("\u25D0", "o"),
4483
+ error: s("", "×"),
4484
+ fatal: s("", "×"),
4485
+ ready: s("", ""),
4486
+ warn: s("", ""),
4487
+ info: s("", "i"),
4488
+ success: s("", ""),
4489
+ debug: s("", "D"),
4490
+ trace: s("", ""),
4491
+ fail: s("", "×"),
4492
+ start: s("", "o"),
4490
4493
  log: ""
4491
4494
  };
4492
4495
  FancyReporter = class FancyReporter extends BasicReporter {
@@ -4540,10 +4543,56 @@ ${indent}`);
4540
4543
  consola = createConsola2();
4541
4544
  });
4542
4545
 
4546
+ // src/cli/prompt.ts
4547
+ async function promptIfMissing(value, message) {
4548
+ if (value)
4549
+ return value;
4550
+ if (!process.stdin.isTTY) {
4551
+ throw new Error(`Missing required argument. Use --help for usage info.`);
4552
+ }
4553
+ const result = await consola.prompt(message, { type: "text" });
4554
+ if (typeof result !== "string" || !result.trim()) {
4555
+ throw new Error("No input provided.");
4556
+ }
4557
+ return result.trim();
4558
+ }
4559
+ async function promptConfirm(message, skipPrompt = false) {
4560
+ if (skipPrompt)
4561
+ return true;
4562
+ if (!process.stdin.isTTY) {
4563
+ throw new Error("Destructive action requires confirmation. Use --yes to skip in non-interactive mode.");
4564
+ }
4565
+ const result = await consola.prompt(message, { type: "confirm" });
4566
+ return result === true;
4567
+ }
4568
+ async function promptSelect(message, options) {
4569
+ if (!process.stdin.isTTY) {
4570
+ throw new Error("Interactive selection requires a TTY.");
4571
+ }
4572
+ const result = await consola.prompt(message, {
4573
+ type: "select",
4574
+ options: options.map((o3) => ({ label: o3.label, value: o3.value }))
4575
+ });
4576
+ return result;
4577
+ }
4578
+ async function promptMultiSelect(message, options) {
4579
+ if (!process.stdin.isTTY) {
4580
+ throw new Error("Interactive selection requires a TTY.");
4581
+ }
4582
+ const result = await consola.prompt(message, {
4583
+ type: "multiselect",
4584
+ options: options.map((o3) => ({ label: o3.label, value: o3.value }))
4585
+ });
4586
+ return result;
4587
+ }
4588
+ var init_prompt2 = __esm(() => {
4589
+ init_dist2();
4590
+ });
4591
+
4543
4592
  // src/cli/config.ts
4544
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
4545
- import { homedir } from "os";
4546
- import { dirname, isAbsolute, join, resolve } from "path";
4593
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
4594
+ import { homedir } from "node:os";
4595
+ import { dirname, isAbsolute, join, resolve } from "node:path";
4547
4596
  function normalizeServiceConfig(service) {
4548
4597
  if (!service)
4549
4598
  return;
@@ -4581,12 +4630,15 @@ function getEnvConfig() {
4581
4630
  const baseUrl = process.env[`TSARR_${upper}_URL`];
4582
4631
  const apiKey = process.env[`TSARR_${upper}_API_KEY`];
4583
4632
  const timeout = process.env[`TSARR_${upper}_TIMEOUT`];
4584
- if (baseUrl || apiKey) {
4585
- services[service] = {
4586
- baseUrl: baseUrl ?? "",
4587
- apiKey: apiKey ?? "",
4588
- ...timeout ? { timeout: Number(timeout) } : {}
4589
- };
4633
+ const partial = {};
4634
+ if (baseUrl)
4635
+ partial.baseUrl = baseUrl;
4636
+ if (apiKey)
4637
+ partial.apiKey = apiKey;
4638
+ if (timeout)
4639
+ partial.timeout = Number(timeout);
4640
+ if (Object.keys(partial).length > 0) {
4641
+ services[service] = partial;
4590
4642
  }
4591
4643
  }
4592
4644
  return Object.keys(services).length ? { services } : {};
@@ -4748,6 +4800,8 @@ function detectFormat(args) {
4748
4800
  return "quiet";
4749
4801
  if (args.json)
4750
4802
  return "json";
4803
+ if (args.plain)
4804
+ return "plain";
4751
4805
  if (args.table)
4752
4806
  return "table";
4753
4807
  return process.stdout.isTTY ? "table" : "json";
@@ -4757,9 +4811,11 @@ function formatOutput(data, options) {
4757
4811
  return;
4758
4812
  }
4759
4813
  switch (options.format) {
4760
- case "json":
4761
- console.log(JSON.stringify(data, null, 2));
4814
+ case "json": {
4815
+ const output = options.select ? selectFields(data, options.select) : data;
4816
+ console.log(JSON.stringify(output, null, 2));
4762
4817
  break;
4818
+ }
4763
4819
  case "quiet": {
4764
4820
  const items = Array.isArray(data) ? data : [data];
4765
4821
  const field = options.idField ?? "id";
@@ -4769,12 +4825,44 @@ function formatOutput(data, options) {
4769
4825
  }
4770
4826
  break;
4771
4827
  }
4828
+ case "plain":
4829
+ printPlain(data, options.columns);
4830
+ break;
4772
4831
  case "table":
4773
- printTable(data, options.columns);
4832
+ printTable(data, options.columns, options.noHeader);
4774
4833
  break;
4775
4834
  }
4776
4835
  }
4777
- function printTable(data, columns) {
4836
+ function selectFields(data, select) {
4837
+ const fields = select.split(",").map((f3) => f3.trim());
4838
+ if (Array.isArray(data)) {
4839
+ return data.map((item) => pickFields(item, fields));
4840
+ }
4841
+ return pickFields(data, fields);
4842
+ }
4843
+ function pickFields(item, fields) {
4844
+ if (item == null || typeof item !== "object")
4845
+ return {};
4846
+ const result = {};
4847
+ for (const field of fields) {
4848
+ result[field] = item[field];
4849
+ }
4850
+ return result;
4851
+ }
4852
+ function printPlain(data, columns) {
4853
+ const items = Array.isArray(data) ? data : [data];
4854
+ if (items.length === 0)
4855
+ return;
4856
+ const cols = columns ?? Object.keys(items[0] ?? {}).slice(0, 8);
4857
+ if (cols.length === 0)
4858
+ return;
4859
+ console.log(cols.join("\t"));
4860
+ for (const item of items) {
4861
+ const row = cols.map((col) => formatCellPlain(item?.[col])).join("\t");
4862
+ console.log(row);
4863
+ }
4864
+ }
4865
+ function printTable(data, columns, noHeader) {
4778
4866
  const items = Array.isArray(data) ? data : [data];
4779
4867
  if (items.length === 0) {
4780
4868
  console.log("No results.");
@@ -4783,79 +4871,121 @@ function printTable(data, columns) {
4783
4871
  const cols = columns ?? Object.keys(items[0] ?? {}).slice(0, 8);
4784
4872
  if (cols.length === 0)
4785
4873
  return;
4874
+ const formattedRows = items.map((item) => cols.map((col) => formatCell(col, item?.[col])));
4786
4875
  const maxColWidth = 60;
4787
- const widths = cols.map((col) => {
4788
- const values = items.map((item) => formatCell(item?.[col]));
4789
- return Math.min(maxColWidth, Math.max(col.length, ...values.map((v2) => v2.length)));
4876
+ const headers = cols.map((col) => formatHeader(col));
4877
+ const widths = cols.map((_3, i2) => {
4878
+ const values = formattedRows.map((row) => stripAnsi3(row[i2]).length);
4879
+ return Math.min(maxColWidth, Math.max(headers[i2].length, ...values));
4790
4880
  });
4791
- const header = cols.map((col, i2) => col.toUpperCase().padEnd(widths[i2])).join(" ");
4792
- console.log(header);
4793
- console.log(cols.map((_3, i2) => "\u2500".repeat(widths[i2])).join(" "));
4794
- for (const item of items) {
4795
- const row = cols.map((col, i2) => {
4796
- const cell = formatCell(item?.[col]);
4797
- const truncated = cell.length > widths[i2] ? `${cell.slice(0, widths[i2] - 1)}\u2026` : cell;
4798
- return truncated.padEnd(widths[i2]);
4881
+ if (!noHeader) {
4882
+ const header = headers.map((h2, i2) => h2.padEnd(widths[i2])).join(" ");
4883
+ console.log(header);
4884
+ console.log(cols.map((_3, i2) => "─".repeat(widths[i2])).join(" "));
4885
+ }
4886
+ for (const row of formattedRows) {
4887
+ const line = row.map((cell, i2) => {
4888
+ const visible = stripAnsi3(cell).length;
4889
+ const truncated = visible > widths[i2] ? truncateWithAnsi(cell, widths[i2]) : cell;
4890
+ const pad = widths[i2] - stripAnsi3(truncated).length;
4891
+ return truncated + " ".repeat(Math.max(0, pad));
4799
4892
  }).join(" ");
4800
- console.log(row);
4893
+ console.log(line);
4801
4894
  }
4802
4895
  console.log(`
4803
4896
  ${items.length} result${items.length === 1 ? "" : "s"}`);
4804
4897
  }
4805
- function formatCell(value) {
4898
+ function formatHeader(col) {
4899
+ return col.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/_/g, " ").toUpperCase();
4900
+ }
4901
+ function formatCell(column, value) {
4902
+ if (value == null)
4903
+ return "—";
4904
+ if (typeof value === "boolean") {
4905
+ return value ? `${GREEN}✓${RESET}` : `${RED}✗${RESET}`;
4906
+ }
4907
+ if (column === "status") {
4908
+ return formatStatus(String(value));
4909
+ }
4910
+ if (typeof value === "number" && (column.toLowerCase().includes("size") || column === "freeSpace" || column === "sizeleft")) {
4911
+ return formatBytes(value);
4912
+ }
4913
+ if (column.toLowerCase().includes("date") || column === "createdAt" || column === "updatedAt") {
4914
+ return formatDate(value);
4915
+ }
4916
+ if (typeof value === "object")
4917
+ return JSON.stringify(value);
4918
+ return String(value);
4919
+ }
4920
+ function formatCellPlain(value) {
4806
4921
  if (value == null)
4807
- return "\u2014";
4922
+ return "";
4808
4923
  if (typeof value === "boolean")
4809
- return value ? "yes" : "no";
4924
+ return value ? "true" : "false";
4810
4925
  if (typeof value === "object")
4811
4926
  return JSON.stringify(value);
4812
4927
  return String(value);
4813
4928
  }
4814
-
4815
- // src/cli/prompt.ts
4816
- async function promptIfMissing(value, message) {
4817
- if (value)
4818
- return value;
4819
- if (!process.stdin.isTTY) {
4820
- throw new Error(`Missing required argument. Use --help for usage info.`);
4929
+ function formatStatus(status) {
4930
+ const lower = status.toLowerCase();
4931
+ if (lower === "ok" || lower === "available" || lower === "ended" || lower === "continuing") {
4932
+ return `${GREEN}${status}${RESET}`;
4821
4933
  }
4822
- const result = await consola.prompt(message, { type: "text" });
4823
- if (typeof result !== "string" || !result.trim()) {
4824
- throw new Error("No input provided.");
4934
+ if (lower === "fail" || lower === "missing" || lower === "not configured") {
4935
+ return `${RED}${status}${RESET}`;
4825
4936
  }
4826
- return result.trim();
4827
- }
4828
- async function promptConfirm(message, skipPrompt = false) {
4829
- if (skipPrompt)
4830
- return true;
4831
- if (!process.stdin.isTTY) {
4832
- throw new Error("Destructive action requires confirmation. Use --yes to skip in non-interactive mode.");
4937
+ if (lower === "warning" || lower === "downloading" || lower === "queued") {
4938
+ return `${YELLOW}${status}${RESET}`;
4833
4939
  }
4834
- const result = await consola.prompt(message, { type: "confirm" });
4835
- return result === true;
4940
+ return status;
4836
4941
  }
4837
- async function promptSelect(message, options) {
4838
- if (!process.stdin.isTTY) {
4839
- throw new Error("Interactive selection requires a TTY.");
4942
+ function formatBytes(bytes) {
4943
+ if (bytes === 0)
4944
+ return "0 B";
4945
+ const units = ["B", "KB", "MB", "GB", "TB"];
4946
+ const i2 = Math.floor(Math.log(bytes) / Math.log(1024));
4947
+ const val = bytes / 1024 ** i2;
4948
+ return `${val < 10 ? val.toFixed(1) : Math.round(val)} ${units[i2]}`;
4949
+ }
4950
+ function formatDate(value) {
4951
+ if (typeof value !== "string" && !(value instanceof Date))
4952
+ return String(value);
4953
+ try {
4954
+ const d2 = new Date(value);
4955
+ if (Number.isNaN(d2.getTime()))
4956
+ return String(value);
4957
+ return d2.toLocaleDateString("en-US", { year: "numeric", month: "short", day: "numeric" });
4958
+ } catch {
4959
+ return String(value);
4840
4960
  }
4841
- const result = await consola.prompt(message, {
4842
- type: "select",
4843
- options: options.map((o3) => ({ label: o3.label, value: o3.value }))
4844
- });
4845
- return result;
4846
4961
  }
4847
- async function promptMultiSelect(message, options) {
4848
- if (!process.stdin.isTTY) {
4849
- throw new Error("Interactive selection requires a TTY.");
4962
+ function stripAnsi3(str) {
4963
+ return str.replace(ANSI_PATTERN, "");
4964
+ }
4965
+ function truncateWithAnsi(str, maxWidth) {
4966
+ let visible = 0;
4967
+ let i2 = 0;
4968
+ while (i2 < str.length && visible < maxWidth - 1) {
4969
+ if (str[i2] === ESC) {
4970
+ const end = str.indexOf("m", i2);
4971
+ if (end !== -1) {
4972
+ i2 = end + 1;
4973
+ continue;
4974
+ }
4975
+ }
4976
+ visible++;
4977
+ i2++;
4850
4978
  }
4851
- const result = await consola.prompt(message, {
4852
- type: "multiselect",
4853
- options: options.map((o3) => ({ label: o3.label, value: o3.value }))
4854
- });
4855
- return result;
4979
+ return `${str.slice(0, i2)}…${RESET}`;
4856
4980
  }
4857
- var init_prompt2 = __esm(() => {
4858
- init_dist2();
4981
+ var ESC, GREEN, RED, YELLOW, RESET, ANSI_PATTERN;
4982
+ var init_output = __esm(() => {
4983
+ ESC = String.fromCharCode(27);
4984
+ GREEN = `${ESC}[32m`;
4985
+ RED = `${ESC}[31m`;
4986
+ YELLOW = `${ESC}[33m`;
4987
+ RESET = `${ESC}[0m`;
4988
+ ANSI_PATTERN = new RegExp(`${String.fromCharCode(27)}\\[[0-9;]*m`, "g");
4859
4989
  });
4860
4990
 
4861
4991
  // src/cli/commands/service.ts
@@ -4872,7 +5002,13 @@ function buildServiceCommand(serviceName, description, clientFactory, resources)
4872
5002
  args: {
4873
5003
  json: { type: "boolean", description: "Output as JSON" },
4874
5004
  table: { type: "boolean", description: "Output as table" },
5005
+ plain: { type: "boolean", description: "Output as TSV (no colors, for piping)" },
4875
5006
  quiet: { type: "boolean", alias: "q", description: "Output IDs only" },
5007
+ "no-header": { type: "boolean", description: "Hide table header row" },
5008
+ select: {
5009
+ type: "string",
5010
+ description: "Cherry-pick fields (comma-separated, JSON mode)"
5011
+ },
4876
5012
  yes: { type: "boolean", alias: "y", description: "Skip confirmation prompts" },
4877
5013
  ...(action.args ?? []).reduce((acc, arg) => {
4878
5014
  acc[arg.name] = {
@@ -4936,7 +5072,9 @@ Run \`tsarr config init\` or set TSARR_${serviceName.toUpperCase()}_API_KEY`);
4936
5072
  formatOutput(result, {
4937
5073
  format,
4938
5074
  columns: action.columns,
4939
- idField: action.idField
5075
+ idField: action.idField,
5076
+ noHeader: !!args["no-header"],
5077
+ select: args.select
4940
5078
  });
4941
5079
  } catch (error) {
4942
5080
  handleError(error, serviceName);
@@ -4983,6 +5121,7 @@ var init_service = __esm(() => {
4983
5121
  init_dist2();
4984
5122
  init_errors();
4985
5123
  init_config();
5124
+ init_output();
4986
5125
  init_prompt2();
4987
5126
  });
4988
5127
 
@@ -4994,6 +5133,7 @@ __export(exports_radarr3, {
4994
5133
  var resources, radarr;
4995
5134
  var init_radarr3 = __esm(() => {
4996
5135
  init_radarr2();
5136
+ init_prompt2();
4997
5137
  init_service();
4998
5138
  resources = [
4999
5139
  {
@@ -5020,6 +5160,79 @@ var init_radarr3 = __esm(() => {
5020
5160
  idField: "tmdbId",
5021
5161
  run: (c3, a2) => c3.searchMovies(a2.term)
5022
5162
  },
5163
+ {
5164
+ name: "add",
5165
+ description: "Search and add a movie",
5166
+ args: [{ name: "term", description: "Search term", required: true }],
5167
+ run: async (c3, a2) => {
5168
+ const searchResult = await c3.searchMovies(a2.term);
5169
+ const results = searchResult?.data ?? searchResult;
5170
+ if (!Array.isArray(results) || results.length === 0) {
5171
+ throw new Error("No movies found.");
5172
+ }
5173
+ const movieId = await promptSelect("Select a movie:", results.map((m2) => ({ label: `${m2.title} (${m2.year})`, value: String(m2.tmdbId) })));
5174
+ const movie = results.find((m2) => String(m2.tmdbId) === movieId);
5175
+ if (!movie) {
5176
+ throw new Error("Selected movie was not found in the search results.");
5177
+ }
5178
+ const profilesResult = await c3.getQualityProfiles();
5179
+ const profiles = profilesResult?.data ?? profilesResult;
5180
+ if (!Array.isArray(profiles) || profiles.length === 0) {
5181
+ throw new Error("No quality profiles found. Configure one in Radarr first.");
5182
+ }
5183
+ const profileId = await promptSelect("Select quality profile:", profiles.map((p) => ({ label: p.name, value: String(p.id) })));
5184
+ const foldersResult = await c3.getRootFolders();
5185
+ const folders = foldersResult?.data ?? foldersResult;
5186
+ if (!Array.isArray(folders) || folders.length === 0) {
5187
+ throw new Error("No root folders found. Configure one in Radarr first.");
5188
+ }
5189
+ const rootFolderPath = await promptSelect("Select root folder:", folders.map((f3) => ({ label: f3.path, value: f3.path })));
5190
+ const confirmed = await promptConfirm(`Add "${movie.title} (${movie.year})"?`, !!a2.yes);
5191
+ if (!confirmed)
5192
+ throw new Error("Cancelled.");
5193
+ return c3.addMovie({
5194
+ ...movie,
5195
+ qualityProfileId: Number(profileId),
5196
+ rootFolderPath,
5197
+ monitored: true,
5198
+ addOptions: { searchForMovie: true }
5199
+ });
5200
+ }
5201
+ },
5202
+ {
5203
+ name: "edit",
5204
+ description: "Edit a movie",
5205
+ args: [
5206
+ { name: "id", description: "Movie ID", required: true, type: "number" },
5207
+ { name: "monitored", description: "Set monitored (true/false)" },
5208
+ { name: "quality-profile-id", description: "Quality profile ID", type: "number" },
5209
+ { name: "tags", description: "Comma-separated tag IDs" }
5210
+ ],
5211
+ run: async (c3, a2) => {
5212
+ const result = await c3.getMovie(a2.id);
5213
+ const movie = result?.data ?? result;
5214
+ const updates = { ...movie };
5215
+ if (a2.monitored !== undefined)
5216
+ updates.monitored = a2.monitored === "true";
5217
+ if (a2["quality-profile-id"] !== undefined)
5218
+ updates.qualityProfileId = Number(a2["quality-profile-id"]);
5219
+ if (a2.tags !== undefined)
5220
+ updates.tags = a2.tags.split(",").map((t2) => Number(t2.trim()));
5221
+ return c3.updateMovie(a2.id, updates);
5222
+ }
5223
+ },
5224
+ {
5225
+ name: "refresh",
5226
+ description: "Refresh movie metadata",
5227
+ args: [{ name: "id", description: "Movie ID", required: true, type: "number" }],
5228
+ run: (c3, a2) => c3.runCommand({ name: "RefreshMovie", movieIds: [a2.id] })
5229
+ },
5230
+ {
5231
+ name: "manual-search",
5232
+ description: "Trigger a manual search for releases",
5233
+ args: [{ name: "id", description: "Movie ID", required: true, type: "number" }],
5234
+ run: (c3, a2) => c3.runCommand({ name: "MoviesSearch", movieIds: [a2.id] })
5235
+ },
5023
5236
  {
5024
5237
  name: "delete",
5025
5238
  description: "Delete a movie",
@@ -5352,7 +5565,7 @@ var separatorArrayExplode2 = (style) => {
5352
5565
  return "";
5353
5566
  }
5354
5567
  if (typeof value === "object") {
5355
- throw new Error("Deeply-nested arrays/objects aren\u2019t supported. Provide your own `querySerializer()` to handle these.");
5568
+ throw new Error("Deeply-nested arrays/objects aren’t supported. Provide your own `querySerializer()` to handle these.");
5356
5569
  }
5357
5570
  return `${name}=${allowReserved ? value : encodeURIComponent(value)}`;
5358
5571
  }, serializeObjectParam2 = ({
@@ -7503,6 +7716,10 @@ class SonarrClient {
7503
7716
  updateConfig(newConfig) {
7504
7717
  const updatedConfig = { ...this.clientConfig.config, ...newConfig };
7505
7718
  this.clientConfig = createServarrClient(updatedConfig);
7719
+ client2.setConfig({
7720
+ baseUrl: this.clientConfig.getBaseUrl(),
7721
+ headers: this.clientConfig.getHeaders()
7722
+ });
7506
7723
  return this.clientConfig.config;
7507
7724
  }
7508
7725
  }
@@ -7520,6 +7737,7 @@ __export(exports_sonarr3, {
7520
7737
  var resources2, sonarr;
7521
7738
  var init_sonarr3 = __esm(() => {
7522
7739
  init_sonarr2();
7740
+ init_prompt2();
7523
7741
  init_service();
7524
7742
  resources2 = [
7525
7743
  {
@@ -7545,6 +7763,79 @@ var init_sonarr3 = __esm(() => {
7545
7763
  columns: ["tvdbId", "title", "year", "overview"],
7546
7764
  run: (c3, a2) => c3.searchSeries(a2.term)
7547
7765
  },
7766
+ {
7767
+ name: "add",
7768
+ description: "Search and add a series",
7769
+ args: [{ name: "term", description: "Search term", required: true }],
7770
+ run: async (c3, a2) => {
7771
+ const searchResult = await c3.searchSeries(a2.term);
7772
+ const results = searchResult?.data ?? searchResult;
7773
+ if (!Array.isArray(results) || results.length === 0) {
7774
+ throw new Error("No series found.");
7775
+ }
7776
+ const seriesId = await promptSelect("Select a series:", results.map((s2) => ({ label: `${s2.title} (${s2.year})`, value: String(s2.tvdbId) })));
7777
+ const series = results.find((s2) => String(s2.tvdbId) === seriesId);
7778
+ if (!series) {
7779
+ throw new Error("Selected series was not found in the search results.");
7780
+ }
7781
+ const profilesResult = await c3.getQualityProfiles();
7782
+ const profiles = profilesResult?.data ?? profilesResult;
7783
+ if (!Array.isArray(profiles) || profiles.length === 0) {
7784
+ throw new Error("No quality profiles found. Configure one in Sonarr first.");
7785
+ }
7786
+ const profileId = await promptSelect("Select quality profile:", profiles.map((p) => ({ label: p.name, value: String(p.id) })));
7787
+ const foldersResult = await c3.getRootFolders();
7788
+ const folders = foldersResult?.data ?? foldersResult;
7789
+ if (!Array.isArray(folders) || folders.length === 0) {
7790
+ throw new Error("No root folders found. Configure one in Sonarr first.");
7791
+ }
7792
+ const rootFolderPath = await promptSelect("Select root folder:", folders.map((f3) => ({ label: f3.path, value: f3.path })));
7793
+ const confirmed = await promptConfirm(`Add "${series.title} (${series.year})"?`, !!a2.yes);
7794
+ if (!confirmed)
7795
+ throw new Error("Cancelled.");
7796
+ return c3.addSeries({
7797
+ ...series,
7798
+ qualityProfileId: Number(profileId),
7799
+ rootFolderPath,
7800
+ monitored: true,
7801
+ addOptions: { searchForMissingEpisodes: true }
7802
+ });
7803
+ }
7804
+ },
7805
+ {
7806
+ name: "edit",
7807
+ description: "Edit a series",
7808
+ args: [
7809
+ { name: "id", description: "Series ID", required: true, type: "number" },
7810
+ { name: "monitored", description: "Set monitored (true/false)" },
7811
+ { name: "quality-profile-id", description: "Quality profile ID", type: "number" },
7812
+ { name: "tags", description: "Comma-separated tag IDs" }
7813
+ ],
7814
+ run: async (c3, a2) => {
7815
+ const result = await c3.getSeriesById(a2.id);
7816
+ const series = result?.data ?? result;
7817
+ const updates = { ...series };
7818
+ if (a2.monitored !== undefined)
7819
+ updates.monitored = a2.monitored === "true";
7820
+ if (a2["quality-profile-id"] !== undefined)
7821
+ updates.qualityProfileId = Number(a2["quality-profile-id"]);
7822
+ if (a2.tags !== undefined)
7823
+ updates.tags = a2.tags.split(",").map((t2) => Number(t2.trim()));
7824
+ return c3.updateSeries(String(a2.id), updates);
7825
+ }
7826
+ },
7827
+ {
7828
+ name: "refresh",
7829
+ description: "Refresh series metadata",
7830
+ args: [{ name: "id", description: "Series ID", required: true, type: "number" }],
7831
+ run: (c3, a2) => c3.runCommand({ name: "RefreshSeries", seriesId: a2.id })
7832
+ },
7833
+ {
7834
+ name: "manual-search",
7835
+ description: "Trigger a manual search for releases",
7836
+ args: [{ name: "id", description: "Series ID", required: true, type: "number" }],
7837
+ run: (c3, a2) => c3.runCommand({ name: "SeriesSearch", seriesId: a2.id })
7838
+ },
7548
7839
  {
7549
7840
  name: "delete",
7550
7841
  description: "Delete a series",
@@ -7848,7 +8139,7 @@ var separatorArrayExplode3 = (style) => {
7848
8139
  return "";
7849
8140
  }
7850
8141
  if (typeof value === "object") {
7851
- throw new Error("Deeply-nested arrays/objects aren\u2019t supported. Provide your own `querySerializer()` to handle these.");
8142
+ throw new Error("Deeply-nested arrays/objects aren’t supported. Provide your own `querySerializer()` to handle these.");
7852
8143
  }
7853
8144
  return `${name}=${allowReserved ? value : encodeURIComponent(value)}`;
7854
8145
  }, serializeObjectParam3 = ({
@@ -9943,6 +10234,10 @@ class LidarrClient {
9943
10234
  updateConfig(newConfig) {
9944
10235
  const updatedConfig = { ...this.clientConfig.config, ...newConfig };
9945
10236
  this.clientConfig = createServarrClient(updatedConfig);
10237
+ client3.setConfig({
10238
+ baseUrl: this.clientConfig.getBaseUrl(),
10239
+ headers: this.clientConfig.getHeaders()
10240
+ });
9946
10241
  return this.clientConfig.config;
9947
10242
  }
9948
10243
  }
@@ -9960,6 +10255,7 @@ __export(exports_lidarr3, {
9960
10255
  var resources3, lidarr;
9961
10256
  var init_lidarr3 = __esm(() => {
9962
10257
  init_lidarr2();
10258
+ init_prompt2();
9963
10259
  init_service();
9964
10260
  resources3 = [
9965
10261
  {
@@ -9983,8 +10279,85 @@ var init_lidarr3 = __esm(() => {
9983
10279
  description: "Search for artists",
9984
10280
  args: [{ name: "term", description: "Search term", required: true }],
9985
10281
  columns: ["foreignArtistId", "artistName", "overview"],
10282
+ idField: "foreignArtistId",
9986
10283
  run: (c3, a2) => c3.searchArtists(a2.term)
9987
10284
  },
10285
+ {
10286
+ name: "add",
10287
+ description: "Search and add an artist",
10288
+ args: [{ name: "term", description: "Search term", required: true }],
10289
+ run: async (c3, a2) => {
10290
+ const searchResult = await c3.searchArtists(a2.term);
10291
+ const results = searchResult?.data ?? searchResult;
10292
+ if (!Array.isArray(results) || results.length === 0) {
10293
+ throw new Error("No artists found.");
10294
+ }
10295
+ const artistId = await promptSelect("Select an artist:", results.map((ar) => ({
10296
+ label: ar.artistName,
10297
+ value: String(ar.foreignArtistId)
10298
+ })));
10299
+ const artist = results.find((ar) => String(ar.foreignArtistId) === artistId);
10300
+ if (!artist) {
10301
+ throw new Error("Selected artist was not found in the search results.");
10302
+ }
10303
+ const profilesResult = await c3.getQualityProfiles();
10304
+ const profiles = profilesResult?.data ?? profilesResult;
10305
+ if (!Array.isArray(profiles) || profiles.length === 0) {
10306
+ throw new Error("No quality profiles found. Configure one in Lidarr first.");
10307
+ }
10308
+ const profileId = await promptSelect("Select quality profile:", profiles.map((p) => ({ label: p.name, value: String(p.id) })));
10309
+ const foldersResult = await c3.getRootFolders();
10310
+ const folders = foldersResult?.data ?? foldersResult;
10311
+ if (!Array.isArray(folders) || folders.length === 0) {
10312
+ throw new Error("No root folders found. Configure one in Lidarr first.");
10313
+ }
10314
+ const rootFolderPath = await promptSelect("Select root folder:", folders.map((f3) => ({ label: f3.path, value: f3.path })));
10315
+ const confirmed = await promptConfirm(`Add "${artist.artistName}"?`, !!a2.yes);
10316
+ if (!confirmed)
10317
+ throw new Error("Cancelled.");
10318
+ return c3.addArtist({
10319
+ ...artist,
10320
+ qualityProfileId: Number(profileId),
10321
+ rootFolderPath,
10322
+ monitored: true,
10323
+ addOptions: { searchForMissingAlbums: true }
10324
+ });
10325
+ }
10326
+ },
10327
+ {
10328
+ name: "edit",
10329
+ description: "Edit an artist",
10330
+ args: [
10331
+ { name: "id", description: "Artist ID", required: true, type: "number" },
10332
+ { name: "monitored", description: "Set monitored (true/false)" },
10333
+ { name: "quality-profile-id", description: "Quality profile ID", type: "number" },
10334
+ { name: "tags", description: "Comma-separated tag IDs" }
10335
+ ],
10336
+ run: async (c3, a2) => {
10337
+ const result = await c3.getArtist(a2.id);
10338
+ const artist = result?.data ?? result;
10339
+ const updates = { ...artist };
10340
+ if (a2.monitored !== undefined)
10341
+ updates.monitored = a2.monitored === "true";
10342
+ if (a2["quality-profile-id"] !== undefined)
10343
+ updates.qualityProfileId = Number(a2["quality-profile-id"]);
10344
+ if (a2.tags !== undefined)
10345
+ updates.tags = a2.tags.split(",").map((t2) => Number(t2.trim()));
10346
+ return c3.updateArtist(a2.id, updates);
10347
+ }
10348
+ },
10349
+ {
10350
+ name: "refresh",
10351
+ description: "Refresh artist metadata",
10352
+ args: [{ name: "id", description: "Artist ID", required: true, type: "number" }],
10353
+ run: (c3, a2) => c3.runCommand({ name: "RefreshArtist", artistId: a2.id })
10354
+ },
10355
+ {
10356
+ name: "manual-search",
10357
+ description: "Trigger a manual search for releases",
10358
+ args: [{ name: "id", description: "Artist ID", required: true, type: "number" }],
10359
+ run: (c3, a2) => c3.runCommand({ name: "ArtistSearch", artistId: a2.id })
10360
+ },
9988
10361
  {
9989
10362
  name: "delete",
9990
10363
  description: "Delete an artist",
@@ -10015,6 +10388,7 @@ var init_lidarr3 = __esm(() => {
10015
10388
  description: "Search for albums",
10016
10389
  args: [{ name: "term", description: "Search term", required: true }],
10017
10390
  columns: ["foreignAlbumId", "title", "artistId"],
10391
+ idField: "foreignAlbumId",
10018
10392
  run: (c3, a2) => c3.searchAlbums(a2.term)
10019
10393
  }
10020
10394
  ]
@@ -10295,7 +10669,7 @@ var separatorArrayExplode4 = (style) => {
10295
10669
  return "";
10296
10670
  }
10297
10671
  if (typeof value === "object") {
10298
- throw new Error("Deeply-nested arrays/objects aren\u2019t supported. Provide your own `querySerializer()` to handle these.");
10672
+ throw new Error("Deeply-nested arrays/objects aren’t supported. Provide your own `querySerializer()` to handle these.");
10299
10673
  }
10300
10674
  return `${name}=${allowReserved ? value : encodeURIComponent(value)}`;
10301
10675
  }, serializeObjectParam4 = ({
@@ -12336,6 +12710,10 @@ class ReadarrClient {
12336
12710
  updateConfig(newConfig) {
12337
12711
  const updatedConfig = { ...this.clientConfig.config, ...newConfig };
12338
12712
  this.clientConfig = createServarrClient(updatedConfig);
12713
+ client4.setConfig({
12714
+ baseUrl: this.clientConfig.getBaseUrl(),
12715
+ headers: this.clientConfig.getHeaders()
12716
+ });
12339
12717
  return this.clientConfig.config;
12340
12718
  }
12341
12719
  }
@@ -12353,6 +12731,7 @@ __export(exports_readarr3, {
12353
12731
  var resources4, readarr;
12354
12732
  var init_readarr3 = __esm(() => {
12355
12733
  init_readarr2();
12734
+ init_prompt2();
12356
12735
  init_service();
12357
12736
  resources4 = [
12358
12737
  {
@@ -12378,6 +12757,79 @@ var init_readarr3 = __esm(() => {
12378
12757
  columns: ["foreignAuthorId", "authorName", "overview"],
12379
12758
  run: (c3, a2) => c3.searchAuthors(a2.term)
12380
12759
  },
12760
+ {
12761
+ name: "add",
12762
+ description: "Search and add an author",
12763
+ args: [{ name: "term", description: "Search term", required: true }],
12764
+ run: async (c3, a2) => {
12765
+ const searchResult = await c3.searchAuthors(a2.term);
12766
+ const results = searchResult?.data ?? searchResult;
12767
+ if (!Array.isArray(results) || results.length === 0) {
12768
+ throw new Error("No authors found.");
12769
+ }
12770
+ const authorId = await promptSelect("Select an author:", results.map((au) => ({
12771
+ label: au.authorName,
12772
+ value: String(au.foreignAuthorId)
12773
+ })));
12774
+ const author = results.find((au) => String(au.foreignAuthorId) === authorId);
12775
+ const profilesResult = await c3.getQualityProfiles();
12776
+ const profiles = profilesResult?.data ?? profilesResult;
12777
+ if (!Array.isArray(profiles) || profiles.length === 0) {
12778
+ throw new Error("No quality profiles found. Configure one in Readarr first.");
12779
+ }
12780
+ const profileId = await promptSelect("Select quality profile:", profiles.map((p) => ({ label: p.name, value: String(p.id) })));
12781
+ const foldersResult = await c3.getRootFolders();
12782
+ const folders = foldersResult?.data ?? foldersResult;
12783
+ if (!Array.isArray(folders) || folders.length === 0) {
12784
+ throw new Error("No root folders found. Configure one in Readarr first.");
12785
+ }
12786
+ const rootFolderPath = await promptSelect("Select root folder:", folders.map((f3) => ({ label: f3.path, value: f3.path })));
12787
+ const confirmed = await promptConfirm(`Add "${author.authorName}"?`, !!a2.yes);
12788
+ if (!confirmed)
12789
+ throw new Error("Cancelled.");
12790
+ return c3.addAuthor({
12791
+ ...author,
12792
+ qualityProfileId: Number(profileId),
12793
+ rootFolderPath,
12794
+ monitored: true,
12795
+ addOptions: { searchForMissingBooks: true }
12796
+ });
12797
+ }
12798
+ },
12799
+ {
12800
+ name: "edit",
12801
+ description: "Edit an author",
12802
+ args: [
12803
+ { name: "id", description: "Author ID", required: true, type: "number" },
12804
+ { name: "monitored", description: "Set monitored (true/false)" },
12805
+ { name: "quality-profile-id", description: "Quality profile ID", type: "number" },
12806
+ { name: "tags", description: "Comma-separated tag IDs" }
12807
+ ],
12808
+ run: async (c3, a2) => {
12809
+ const result = await c3.getAuthor(a2.id);
12810
+ const author = result?.data ?? result;
12811
+ const updates = { ...author };
12812
+ if (a2.monitored !== undefined)
12813
+ updates.monitored = a2.monitored === "true";
12814
+ if (a2["quality-profile-id"] !== undefined)
12815
+ updates.qualityProfileId = Number(a2["quality-profile-id"]);
12816
+ if (a2.tags !== undefined)
12817
+ updates.tags = a2.tags.split(",").map((t2) => Number(t2.trim()));
12818
+ return c3.updateAuthor(a2.id, updates);
12819
+ }
12820
+ },
12821
+ {
12822
+ name: "refresh",
12823
+ description: "Refresh author metadata",
12824
+ args: [{ name: "id", description: "Author ID", required: true, type: "number" }],
12825
+ run: (c3, a2) => c3.runCommand({ name: "RefreshAuthor", authorId: a2.id })
12826
+ },
12827
+ {
12828
+ name: "manual-search",
12829
+ description: "Trigger a manual search for releases",
12830
+ args: [{ name: "id", description: "Author ID", required: true, type: "number" }],
12831
+ run: (c3, a2) => c3.runCommand({ name: "AuthorSearch", authorId: a2.id })
12832
+ },
12381
12833
  {
12382
12834
  name: "delete",
12383
12835
  description: "Delete an author",
@@ -12688,7 +13140,7 @@ var separatorArrayExplode5 = (style) => {
12688
13140
  return "";
12689
13141
  }
12690
13142
  if (typeof value === "object") {
12691
- throw new Error("Deeply-nested arrays/objects aren\u2019t supported. Provide your own `querySerializer()` to handle these.");
13143
+ throw new Error("Deeply-nested arrays/objects aren’t supported. Provide your own `querySerializer()` to handle these.");
12692
13144
  }
12693
13145
  return `${name}=${allowReserved ? value : encodeURIComponent(value)}`;
12694
13146
  }, serializeObjectParam5 = ({
@@ -14060,6 +14512,10 @@ class ProwlarrClient {
14060
14512
  updateConfig(newConfig) {
14061
14513
  const updatedConfig = { ...this.clientConfig.config, ...newConfig };
14062
14514
  this.clientConfig = createServarrClient(updatedConfig);
14515
+ client5.setConfig({
14516
+ baseUrl: this.clientConfig.getBaseUrl(),
14517
+ headers: this.clientConfig.getHeaders()
14518
+ });
14063
14519
  return this.clientConfig.config;
14064
14520
  }
14065
14521
  }
@@ -14411,7 +14867,7 @@ var separatorArrayExplode6 = (style) => {
14411
14867
  return "";
14412
14868
  }
14413
14869
  if (typeof value === "object") {
14414
- throw new Error("Deeply-nested arrays/objects aren\u2019t supported. Provide your own `querySerializer()` to handle these.");
14870
+ throw new Error("Deeply-nested arrays/objects aren’t supported. Provide your own `querySerializer()` to handle these.");
14415
14871
  }
14416
14872
  return `${name}=${allowReserved ? value : encodeURIComponent(value)}`;
14417
14873
  }, serializeObjectParam6 = ({
@@ -14990,46 +15446,46 @@ var init_client7 = __esm(() => {
14990
15446
  var client6;
14991
15447
  var init_client_gen12 = __esm(() => {
14992
15448
  init_client7();
14993
- client6 = createClient6(createConfig6({ baseUrl: "/api" }));
15449
+ client6 = createClient6(createConfig6());
14994
15450
  });
14995
15451
 
14996
15452
  // src/generated/bazarr/sdk.gen.ts
14997
15453
  var getBadges = (options) => (options?.client ?? client6).get({
14998
15454
  security: [{ name: "X-API-KEY", type: "apiKey" }],
14999
- url: "/badges",
15455
+ url: "/api/badges",
15000
15456
  ...options
15001
15457
  }), getEpisodes = (options) => (options?.client ?? client6).get({
15002
15458
  security: [{ name: "X-API-KEY", type: "apiKey" }],
15003
- url: "/episodes",
15459
+ url: "/api/episodes",
15004
15460
  ...options
15005
15461
  }), deleteEpisodesBlacklist = (options) => (options?.client ?? client6).delete({
15006
15462
  security: [{ name: "X-API-KEY", type: "apiKey" }],
15007
- url: "/episodes/blacklist",
15463
+ url: "/api/episodes/blacklist",
15008
15464
  ...options
15009
15465
  }), getEpisodesBlacklist = (options) => (options?.client ?? client6).get({
15010
15466
  security: [{ name: "X-API-KEY", type: "apiKey" }],
15011
- url: "/episodes/blacklist",
15467
+ url: "/api/episodes/blacklist",
15012
15468
  ...options
15013
15469
  }), postEpisodesBlacklist = (options) => (options.client ?? client6).post({
15014
15470
  security: [{ name: "X-API-KEY", type: "apiKey" }],
15015
- url: "/episodes/blacklist",
15471
+ url: "/api/episodes/blacklist",
15016
15472
  ...options
15017
15473
  }), getEpisodesHistory = (options) => (options?.client ?? client6).get({
15018
15474
  security: [{ name: "X-API-KEY", type: "apiKey" }],
15019
- url: "/episodes/history",
15475
+ url: "/api/episodes/history",
15020
15476
  ...options
15021
15477
  }), deleteEpisodesSubtitles = (options) => (options.client ?? client6).delete({
15022
15478
  security: [{ name: "X-API-KEY", type: "apiKey" }],
15023
- url: "/episodes/subtitles",
15479
+ url: "/api/episodes/subtitles",
15024
15480
  ...options
15025
15481
  }), patchEpisodesSubtitles = (options) => (options.client ?? client6).patch({
15026
15482
  security: [{ name: "X-API-KEY", type: "apiKey" }],
15027
- url: "/episodes/subtitles",
15483
+ url: "/api/episodes/subtitles",
15028
15484
  ...options
15029
15485
  }), postEpisodesSubtitles = (options) => (options.client ?? client6).post({
15030
15486
  ...formDataBodySerializer,
15031
15487
  security: [{ name: "X-API-KEY", type: "apiKey" }],
15032
- url: "/episodes/subtitles",
15488
+ url: "/api/episodes/subtitles",
15033
15489
  ...options,
15034
15490
  headers: {
15035
15491
  "Content-Type": null,
@@ -15037,64 +15493,64 @@ var getBadges = (options) => (options?.client ?? client6).get({
15037
15493
  }
15038
15494
  }), getEpisodesWanted = (options) => (options?.client ?? client6).get({
15039
15495
  security: [{ name: "X-API-KEY", type: "apiKey" }],
15040
- url: "/episodes/wanted",
15496
+ url: "/api/episodes/wanted",
15041
15497
  ...options
15042
15498
  }), getBrowseBazarrFs = (options) => (options?.client ?? client6).get({
15043
15499
  security: [{ name: "X-API-KEY", type: "apiKey" }],
15044
- url: "/files",
15500
+ url: "/api/files",
15045
15501
  ...options
15046
15502
  }), getBrowseRadarrFs = (options) => (options?.client ?? client6).get({
15047
15503
  security: [{ name: "X-API-KEY", type: "apiKey" }],
15048
- url: "/files/radarr",
15504
+ url: "/api/files/radarr",
15049
15505
  ...options
15050
15506
  }), getBrowseSonarrFs = (options) => (options?.client ?? client6).get({
15051
15507
  security: [{ name: "X-API-KEY", type: "apiKey" }],
15052
- url: "/files/sonarr",
15508
+ url: "/api/files/sonarr",
15053
15509
  ...options
15054
15510
  }), getHistoryStats = (options) => (options?.client ?? client6).get({
15055
15511
  security: [{ name: "X-API-KEY", type: "apiKey" }],
15056
- url: "/history/stats",
15512
+ url: "/api/history/stats",
15057
15513
  ...options
15058
15514
  }), getMovies = (options) => (options?.client ?? client6).get({
15059
15515
  security: [{ name: "X-API-KEY", type: "apiKey" }],
15060
- url: "/movies",
15516
+ url: "/api/movies",
15061
15517
  ...options
15062
15518
  }), patchMovies = (options) => (options?.client ?? client6).patch({
15063
15519
  security: [{ name: "X-API-KEY", type: "apiKey" }],
15064
- url: "/movies",
15520
+ url: "/api/movies",
15065
15521
  ...options
15066
15522
  }), postMovies = (options) => (options?.client ?? client6).post({
15067
15523
  security: [{ name: "X-API-KEY", type: "apiKey" }],
15068
- url: "/movies",
15524
+ url: "/api/movies",
15069
15525
  ...options
15070
15526
  }), deleteMoviesBlacklist = (options) => (options?.client ?? client6).delete({
15071
15527
  security: [{ name: "X-API-KEY", type: "apiKey" }],
15072
- url: "/movies/blacklist",
15528
+ url: "/api/movies/blacklist",
15073
15529
  ...options
15074
15530
  }), getMoviesBlacklist = (options) => (options?.client ?? client6).get({
15075
15531
  security: [{ name: "X-API-KEY", type: "apiKey" }],
15076
- url: "/movies/blacklist",
15532
+ url: "/api/movies/blacklist",
15077
15533
  ...options
15078
15534
  }), postMoviesBlacklist = (options) => (options.client ?? client6).post({
15079
15535
  security: [{ name: "X-API-KEY", type: "apiKey" }],
15080
- url: "/movies/blacklist",
15536
+ url: "/api/movies/blacklist",
15081
15537
  ...options
15082
15538
  }), getMoviesHistory = (options) => (options?.client ?? client6).get({
15083
15539
  security: [{ name: "X-API-KEY", type: "apiKey" }],
15084
- url: "/movies/history",
15540
+ url: "/api/movies/history",
15085
15541
  ...options
15086
15542
  }), deleteMoviesSubtitles = (options) => (options.client ?? client6).delete({
15087
15543
  security: [{ name: "X-API-KEY", type: "apiKey" }],
15088
- url: "/movies/subtitles",
15544
+ url: "/api/movies/subtitles",
15089
15545
  ...options
15090
15546
  }), patchMoviesSubtitles = (options) => (options.client ?? client6).patch({
15091
15547
  security: [{ name: "X-API-KEY", type: "apiKey" }],
15092
- url: "/movies/subtitles",
15548
+ url: "/api/movies/subtitles",
15093
15549
  ...options
15094
15550
  }), postMoviesSubtitles = (options) => (options.client ?? client6).post({
15095
15551
  ...formDataBodySerializer,
15096
15552
  security: [{ name: "X-API-KEY", type: "apiKey" }],
15097
- url: "/movies/subtitles",
15553
+ url: "/api/movies/subtitles",
15098
15554
  ...options,
15099
15555
  headers: {
15100
15556
  "Content-Type": null,
@@ -15102,151 +15558,147 @@ var getBadges = (options) => (options?.client ?? client6).get({
15102
15558
  }
15103
15559
  }), getMoviesWanted = (options) => (options?.client ?? client6).get({
15104
15560
  security: [{ name: "X-API-KEY", type: "apiKey" }],
15105
- url: "/movies/wanted",
15561
+ url: "/api/movies/wanted",
15106
15562
  ...options
15107
15563
  }), getProviders = (options) => (options?.client ?? client6).get({
15108
15564
  security: [{ name: "X-API-KEY", type: "apiKey" }],
15109
- url: "/providers",
15565
+ url: "/api/providers",
15110
15566
  ...options
15111
15567
  }), postProviders = (options) => (options.client ?? client6).post({
15112
15568
  security: [{ name: "X-API-KEY", type: "apiKey" }],
15113
- url: "/providers",
15569
+ url: "/api/providers",
15114
15570
  ...options
15115
15571
  }), getProviderEpisodes = (options) => (options.client ?? client6).get({
15116
15572
  security: [{ name: "X-API-KEY", type: "apiKey" }],
15117
- url: "/providers/episodes",
15573
+ url: "/api/providers/episodes",
15118
15574
  ...options
15119
15575
  }), postProviderEpisodes = (options) => (options.client ?? client6).post({
15120
15576
  security: [{ name: "X-API-KEY", type: "apiKey" }],
15121
- url: "/providers/episodes",
15577
+ url: "/api/providers/episodes",
15122
15578
  ...options
15123
15579
  }), getProviderMovies = (options) => (options.client ?? client6).get({
15124
15580
  security: [{ name: "X-API-KEY", type: "apiKey" }],
15125
- url: "/providers/movies",
15581
+ url: "/api/providers/movies",
15126
15582
  ...options
15127
15583
  }), postProviderMovies = (options) => (options.client ?? client6).post({
15128
15584
  security: [{ name: "X-API-KEY", type: "apiKey" }],
15129
- url: "/providers/movies",
15585
+ url: "/api/providers/movies",
15130
15586
  ...options
15131
15587
  }), getSeries = (options) => (options?.client ?? client6).get({
15132
15588
  security: [{ name: "X-API-KEY", type: "apiKey" }],
15133
- url: "/series",
15589
+ url: "/api/series",
15134
15590
  ...options
15135
15591
  }), patchSeries = (options) => (options?.client ?? client6).patch({
15136
15592
  security: [{ name: "X-API-KEY", type: "apiKey" }],
15137
- url: "/series",
15593
+ url: "/api/series",
15138
15594
  ...options
15139
15595
  }), postSeries = (options) => (options?.client ?? client6).post({
15140
15596
  security: [{ name: "X-API-KEY", type: "apiKey" }],
15141
- url: "/series",
15597
+ url: "/api/series",
15142
15598
  ...options
15143
15599
  }), getSubtitles = (options) => (options.client ?? client6).get({
15144
15600
  security: [{ name: "X-API-KEY", type: "apiKey" }],
15145
- url: "/subtitles",
15601
+ url: "/api/subtitles",
15146
15602
  ...options
15147
15603
  }), patchSubtitles = (options) => (options.client ?? client6).patch({
15148
15604
  security: [{ name: "X-API-KEY", type: "apiKey" }],
15149
- url: "/subtitles",
15605
+ url: "/api/subtitles",
15150
15606
  ...options
15151
15607
  }), getSubtitleNameInfo = (options) => (options.client ?? client6).get({
15152
15608
  security: [{ name: "X-API-KEY", type: "apiKey" }],
15153
- url: "/subtitles/info",
15609
+ url: "/api/subtitles/info",
15154
15610
  ...options
15155
15611
  }), getSystemAnnouncements = (options) => (options?.client ?? client6).get({
15156
15612
  security: [{ name: "X-API-KEY", type: "apiKey" }],
15157
- url: "/system/announcements",
15613
+ url: "/api/system/announcements",
15158
15614
  ...options
15159
15615
  }), postSystemAnnouncements = (options) => (options.client ?? client6).post({
15160
15616
  security: [{ name: "X-API-KEY", type: "apiKey" }],
15161
- url: "/system/announcements",
15617
+ url: "/api/system/announcements",
15162
15618
  ...options
15163
15619
  }), deleteSystemBackups = (options) => (options.client ?? client6).delete({
15164
15620
  security: [{ name: "X-API-KEY", type: "apiKey" }],
15165
- url: "/system/backups",
15621
+ url: "/api/system/backups",
15166
15622
  ...options
15167
15623
  }), getSystemBackups = (options) => (options?.client ?? client6).get({
15168
15624
  security: [{ name: "X-API-KEY", type: "apiKey" }],
15169
- url: "/system/backups",
15625
+ url: "/api/system/backups",
15170
15626
  ...options
15171
15627
  }), patchSystemBackups = (options) => (options.client ?? client6).patch({
15172
15628
  security: [{ name: "X-API-KEY", type: "apiKey" }],
15173
- url: "/system/backups",
15629
+ url: "/api/system/backups",
15174
15630
  ...options
15175
15631
  }), postSystemBackups = (options) => (options?.client ?? client6).post({
15176
15632
  security: [{ name: "X-API-KEY", type: "apiKey" }],
15177
- url: "/system/backups",
15633
+ url: "/api/system/backups",
15178
15634
  ...options
15179
15635
  }), getSystemHealth = (options) => (options?.client ?? client6).get({
15180
15636
  security: [{ name: "X-API-KEY", type: "apiKey" }],
15181
- url: "/system/health",
15637
+ url: "/api/system/health",
15182
15638
  ...options
15183
15639
  }), deleteSystemJobs = (options) => (options.client ?? client6).delete({
15184
15640
  security: [{ name: "X-API-KEY", type: "apiKey" }],
15185
- url: "/system/jobs",
15641
+ url: "/api/system/jobs",
15186
15642
  ...options
15187
15643
  }), getSystemJobs = (options) => (options?.client ?? client6).get({
15188
15644
  security: [{ name: "X-API-KEY", type: "apiKey" }],
15189
- url: "/system/jobs",
15645
+ url: "/api/system/jobs",
15190
15646
  ...options
15191
15647
  }), patchSystemJobs = (options) => (options.client ?? client6).patch({
15192
15648
  security: [{ name: "X-API-KEY", type: "apiKey" }],
15193
- url: "/system/jobs",
15649
+ url: "/api/system/jobs",
15194
15650
  ...options
15195
15651
  }), postSystemJobs = (options) => (options.client ?? client6).post({
15196
15652
  security: [{ name: "X-API-KEY", type: "apiKey" }],
15197
- url: "/system/jobs",
15653
+ url: "/api/system/jobs",
15198
15654
  ...options
15199
15655
  }), getLanguages = (options) => (options?.client ?? client6).get({
15200
15656
  security: [{ name: "X-API-KEY", type: "apiKey" }],
15201
- url: "/system/languages",
15657
+ url: "/api/system/languages",
15202
15658
  ...options
15203
15659
  }), getLanguagesProfiles = (options) => (options?.client ?? client6).get({
15204
15660
  security: [{ name: "X-API-KEY", type: "apiKey" }],
15205
- url: "/system/languages/profiles",
15661
+ url: "/api/system/languages/profiles",
15206
15662
  ...options
15207
15663
  }), deleteSystemLogs = (options) => (options?.client ?? client6).delete({
15208
15664
  security: [{ name: "X-API-KEY", type: "apiKey" }],
15209
- url: "/system/logs",
15665
+ url: "/api/system/logs",
15210
15666
  ...options
15211
15667
  }), getSystemLogs = (options) => (options?.client ?? client6).get({
15212
15668
  security: [{ name: "X-API-KEY", type: "apiKey" }],
15213
- url: "/system/logs",
15214
- ...options
15215
- }), getSystemPing = (options) => (options?.client ?? client6).get({
15216
- security: [{ name: "X-API-KEY", type: "apiKey" }],
15217
- url: "/system/ping",
15669
+ url: "/api/system/logs",
15218
15670
  ...options
15219
- }), getSystemReleases = (options) => (options?.client ?? client6).get({
15671
+ }), getSystemPing = (options) => (options?.client ?? client6).get({ url: "/api/system/ping", ...options }), getSystemReleases = (options) => (options?.client ?? client6).get({
15220
15672
  security: [{ name: "X-API-KEY", type: "apiKey" }],
15221
- url: "/system/releases",
15673
+ url: "/api/system/releases",
15222
15674
  ...options
15223
15675
  }), getSearches = (options) => (options.client ?? client6).get({
15224
15676
  security: [{ name: "X-API-KEY", type: "apiKey" }],
15225
- url: "/system/searches",
15677
+ url: "/api/system/searches",
15226
15678
  ...options
15227
15679
  }), getSystemStatus = (options) => (options?.client ?? client6).get({
15228
15680
  security: [{ name: "X-API-KEY", type: "apiKey" }],
15229
- url: "/system/status",
15681
+ url: "/api/system/status",
15230
15682
  ...options
15231
15683
  }), getSystemTasks = (options) => (options?.client ?? client6).get({
15232
15684
  security: [{ name: "X-API-KEY", type: "apiKey" }],
15233
- url: "/system/tasks",
15685
+ url: "/api/system/tasks",
15234
15686
  ...options
15235
15687
  }), postSystemTasks = (options) => (options.client ?? client6).post({
15236
15688
  security: [{ name: "X-API-KEY", type: "apiKey" }],
15237
- url: "/system/tasks",
15689
+ url: "/api/system/tasks",
15238
15690
  ...options
15239
15691
  }), postSystemWebhookTest = (options) => (options?.client ?? client6).post({
15240
15692
  security: [{ name: "X-API-KEY", type: "apiKey" }],
15241
- url: "/system/webhooks/test",
15693
+ url: "/api/system/webhooks/test",
15242
15694
  ...options
15243
15695
  }), postWebHooksPlex = (options) => (options.client ?? client6).post({
15244
15696
  security: [{ name: "X-API-KEY", type: "apiKey" }],
15245
- url: "/webhooks/plex",
15697
+ url: "/api/webhooks/plex",
15246
15698
  ...options
15247
15699
  }), postWebHooksRadarr = (options) => (options.client ?? client6).post({
15248
15700
  security: [{ name: "X-API-KEY", type: "apiKey" }],
15249
- url: "/webhooks/radarr",
15701
+ url: "/api/webhooks/radarr",
15250
15702
  ...options,
15251
15703
  headers: {
15252
15704
  "Content-Type": "application/json",
@@ -15254,7 +15706,7 @@ var getBadges = (options) => (options?.client ?? client6).get({
15254
15706
  }
15255
15707
  }), postWebHooksSonarr = (options) => (options.client ?? client6).post({
15256
15708
  security: [{ name: "X-API-KEY", type: "apiKey" }],
15257
- url: "/webhooks/sonarr",
15709
+ url: "/api/webhooks/sonarr",
15258
15710
  ...options,
15259
15711
  headers: {
15260
15712
  "Content-Type": "application/json",
@@ -15743,8 +16195,52 @@ var exports_doctor = {};
15743
16195
  __export(exports_doctor, {
15744
16196
  doctor: () => doctor
15745
16197
  });
15746
- function getDoctorVersion(status) {
15747
- return status?.data?.data?.version ?? status?.data?.data?.bazarr_version ?? status?.data?.version ?? status?.version ?? status?.data?.bazarr_version ?? status?.bazarr_version ?? undefined;
16198
+ function classifyError(error) {
16199
+ if (!(error instanceof Error))
16200
+ return "Unknown error";
16201
+ const msg = error.message;
16202
+ const cause = error.cause;
16203
+ if (cause?.code === "ECONNREFUSED" || msg.includes("ECONNREFUSED")) {
16204
+ return "Connection refused - is the service running?";
16205
+ }
16206
+ if (cause?.code === "ENOTFOUND" || msg.includes("ENOTFOUND")) {
16207
+ return "Host not found - check the URL";
16208
+ }
16209
+ if (cause?.code === "ECONNRESET" || msg.includes("ECONNRESET")) {
16210
+ return "Connection reset - service may have crashed";
16211
+ }
16212
+ if (cause?.code === "ETIMEDOUT" || msg.includes("ETIMEDOUT") || msg.includes("timed out")) {
16213
+ return "Connection timed out - service may be unreachable";
16214
+ }
16215
+ if (msg.includes("fetch failed") || msg.includes("Failed to fetch")) {
16216
+ return `Service unreachable - ${cause?.message ?? "check URL and network"}`;
16217
+ }
16218
+ if (msg.includes("401") || msg.includes("Unauthorized")) {
16219
+ return "Authentication failed (401) - check your API key";
16220
+ }
16221
+ if (msg.includes("403") || msg.includes("Forbidden")) {
16222
+ return "Access denied (403) - check your API key permissions";
16223
+ }
16224
+ if (msg.includes("502") || msg.includes("Bad Gateway")) {
16225
+ return "Bad gateway (502) - reverse proxy or service issue";
16226
+ }
16227
+ if (msg.includes("503") || msg.includes("Service Unavailable")) {
16228
+ return "Service unavailable (503) - service may be starting up";
16229
+ }
16230
+ if (msg.includes("CERT") || msg.includes("certificate") || msg.includes("SSL")) {
16231
+ return "SSL/TLS certificate error - check HTTPS configuration";
16232
+ }
16233
+ return msg;
16234
+ }
16235
+ function extractVersion(service, status) {
16236
+ const data = status?.data ?? status;
16237
+ if (typeof data === "string") {
16238
+ return null;
16239
+ }
16240
+ if (service === "bazarr") {
16241
+ return data?.data?.bazarr_version ?? data?.bazarr_version ?? null;
16242
+ }
16243
+ return data?.version ?? status?.version ?? null;
15748
16244
  }
15749
16245
  var clientFactories, doctor;
15750
16246
  var init_doctor = __esm(() => {
@@ -15757,6 +16253,7 @@ var init_doctor = __esm(() => {
15757
16253
  init_readarr2();
15758
16254
  init_sonarr2();
15759
16255
  init_config();
16256
+ init_output();
15760
16257
  clientFactories = {
15761
16258
  radarr: (c3) => new RadarrClient(c3),
15762
16259
  sonarr: (c3) => new SonarrClient(c3),
@@ -15773,7 +16270,9 @@ var init_doctor = __esm(() => {
15773
16270
  args: {
15774
16271
  json: { type: "boolean", description: "Output as JSON" },
15775
16272
  table: { type: "boolean", description: "Output as table" },
15776
- quiet: { type: "boolean", alias: "q", description: "Output service names only" }
16273
+ plain: { type: "boolean", description: "Output as TSV (no colors, for piping)" },
16274
+ quiet: { type: "boolean", alias: "q", description: "Output service names only" },
16275
+ select: { type: "string", description: "Cherry-pick fields (comma-separated, JSON mode)" }
15777
16276
  },
15778
16277
  async run({ args }) {
15779
16278
  const format = detectFormat(args);
@@ -15808,7 +16307,10 @@ var init_doctor = __esm(() => {
15808
16307
  }
15809
16308
  const client7 = factory(svcConfig);
15810
16309
  const status = await client7.getSystemStatus();
15811
- const version = getDoctorVersion(status) ?? "?";
16310
+ const version = extractVersion(service, status) ?? "?";
16311
+ if (version === "?") {
16312
+ throw new Error("Unexpected response payload");
16313
+ }
15812
16314
  results.push({
15813
16315
  service,
15814
16316
  configured: true,
@@ -15817,24 +16319,28 @@ var init_doctor = __esm(() => {
15817
16319
  baseUrl: svcConfig.baseUrl
15818
16320
  });
15819
16321
  } catch (error) {
15820
- const msg = error instanceof Error ? error.message : "Unknown error";
15821
16322
  results.push({
15822
16323
  service,
15823
16324
  configured: true,
15824
16325
  status: "fail",
15825
16326
  baseUrl: svcConfig.baseUrl,
15826
- error: msg
16327
+ error: classifyError(error)
15827
16328
  });
15828
16329
  }
15829
16330
  }
16331
+ const hadFailure = !hasAny || results.some((r3) => r3.status === "fail");
15830
16332
  if (!hasAny && format === "table") {
15831
16333
  consola.warn("\nNo services configured. Run `tsarr config init` to set up.");
15832
16334
  }
15833
16335
  formatOutput(results, {
15834
16336
  format,
15835
16337
  columns: ["service", "status", "version", "baseUrl", "error"],
15836
- idField: "service"
16338
+ idField: "service",
16339
+ select: args.select
15837
16340
  });
16341
+ if (hadFailure) {
16342
+ process.exitCode = 1;
16343
+ }
15838
16344
  }
15839
16345
  });
15840
16346
  });
@@ -15907,7 +16413,7 @@ var init_config2 = __esm(() => {
15907
16413
  consola.success(`Connected to ${service} v${version}`);
15908
16414
  }
15909
16415
  } catch {
15910
- consola.warn(`Could not connect to ${service} \u2014 config saved anyway.`);
16416
+ consola.warn(`Could not connect to ${service} config saved anyway.`);
15911
16417
  }
15912
16418
  }
15913
16419
  const location = await promptSelect("Save config to:", [
@@ -15940,7 +16446,8 @@ var init_config2 = __esm(() => {
15940
16446
  },
15941
16447
  async run({ args }) {
15942
16448
  setConfigValue(args.key, args.value, !args.local);
15943
- consola.success(`Set ${args.key} = ${args.value}`);
16449
+ const displayValue = /\b(apiKey|apikey|token|secret|password)\b/i.test(args.key) ? "*****" : args.value;
16450
+ consola.success(`Set ${args.key} = ${displayValue}`);
15944
16451
  }
15945
16452
  });
15946
16453
  configGet = defineCommand({
@@ -15968,7 +16475,14 @@ var init_config2 = __esm(() => {
15968
16475
  },
15969
16476
  async run() {
15970
16477
  const config = loadConfig();
15971
- console.log(JSON.stringify(config, null, 2));
16478
+ const redacted = JSON.parse(JSON.stringify(config));
16479
+ if (redacted.services) {
16480
+ for (const svc of Object.values(redacted.services)) {
16481
+ if (svc?.apiKey)
16482
+ svc.apiKey = "*****";
16483
+ }
16484
+ }
16485
+ console.log(JSON.stringify(redacted, null, 2));
15972
16486
  }
15973
16487
  });
15974
16488
  config = defineCommand({
@@ -16095,7 +16609,7 @@ function generateFishCompletion() {
16095
16609
  return `complete -c tsarr -n "__fish_seen_subcommand_from ${svc}; and not __fish_seen_subcommand_from ${res}" -a "${res}"`;
16096
16610
  }).join(`
16097
16611
  `);
16098
- const actionCompletions = Object.entries(SERVICE_COMMANDS).flatMap(([_svc, resources7]) => Object.entries(resources7).map(([res, actions]) => `complete -c tsarr -n "__fish_seen_subcommand_from ${res}; and not __fish_seen_subcommand_from ${actions.join(" ")}" -a "${actions.join(" ")}"`)).join(`
16612
+ const actionCompletions = Object.entries(SERVICE_COMMANDS).flatMap(([svc, resources7]) => Object.entries(resources7).map(([res, actions]) => `complete -c tsarr -n "__fish_seen_subcommand_from ${svc}; and __fish_seen_subcommand_from ${res}; and not __fish_seen_subcommand_from ${actions.join(" ")}" -a "${actions.join(" ")}"`)).join(`
16099
16613
  `);
16100
16614
  return `# Fish completions for tsarr
16101
16615
  set -l services ${services}
@@ -16125,7 +16639,7 @@ var init_completions = __esm(() => {
16125
16639
  init_dist2();
16126
16640
  SERVICE_COMMANDS = {
16127
16641
  radarr: {
16128
- movie: ["list", "get", "search", "delete"],
16642
+ movie: ["list", "get", "search", "add", "edit", "refresh", "manual-search", "delete"],
16129
16643
  profile: ["list", "get"],
16130
16644
  tag: ["list"],
16131
16645
  queue: ["list", "status"],
@@ -16135,7 +16649,7 @@ var init_completions = __esm(() => {
16135
16649
  customformat: ["list"]
16136
16650
  },
16137
16651
  sonarr: {
16138
- series: ["list", "get", "search", "delete"],
16652
+ series: ["list", "get", "search", "add", "edit", "refresh", "manual-search", "delete"],
16139
16653
  episode: ["list", "get"],
16140
16654
  profile: ["list"],
16141
16655
  tag: ["list"],
@@ -16143,7 +16657,7 @@ var init_completions = __esm(() => {
16143
16657
  system: ["status", "health"]
16144
16658
  },
16145
16659
  lidarr: {
16146
- artist: ["list", "get", "search", "delete"],
16660
+ artist: ["list", "get", "search", "add", "edit", "refresh", "manual-search", "delete"],
16147
16661
  album: ["list", "get", "search"],
16148
16662
  profile: ["list"],
16149
16663
  tag: ["list"],
@@ -16151,7 +16665,7 @@ var init_completions = __esm(() => {
16151
16665
  system: ["status", "health"]
16152
16666
  },
16153
16667
  readarr: {
16154
- author: ["list", "get", "search", "delete"],
16668
+ author: ["list", "get", "search", "add", "edit", "refresh", "manual-search", "delete"],
16155
16669
  book: ["list", "get", "search"],
16156
16670
  profile: ["list"],
16157
16671
  tag: ["list"],
@@ -16207,13 +16721,13 @@ var init_completions = __esm(() => {
16207
16721
 
16208
16722
  // src/cli/index.ts
16209
16723
  init_dist();
16210
- import { readFileSync as readFileSync2 } from "fs";
16724
+ import { readFileSync as readFileSync2 } from "node:fs";
16211
16725
  var { version } = JSON.parse(readFileSync2(new URL("../../package.json", import.meta.url), "utf-8"));
16212
16726
  var main = defineCommand({
16213
16727
  meta: {
16214
16728
  name: "tsarr",
16215
16729
  version,
16216
- description: "CLI for Servarr APIs (Radarr, Sonarr, Lidarr, Readarr, Prowlarr, Bazarr)"
16730
+ description: "Type-safe CLI for Servarr APIs (Radarr, Sonarr, Lidarr, Readarr, Prowlarr, Bazarr)"
16217
16731
  },
16218
16732
  subCommands: {
16219
16733
  radarr: () => Promise.resolve().then(() => (init_radarr3(), exports_radarr3)).then((m2) => m2.radarr),