tsarr 2.4.12 → 2.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. package/README.md +15 -3
  2. package/dist/cli/commands/config.d.ts.map +1 -1
  3. package/dist/cli/commands/doctor.d.ts.map +1 -1
  4. package/dist/cli/commands/lidarr.d.ts.map +1 -1
  5. package/dist/cli/commands/qbit.d.ts +4 -0
  6. package/dist/cli/commands/qbit.d.ts.map +1 -0
  7. package/dist/cli/commands/radarr.d.ts.map +1 -1
  8. package/dist/cli/commands/readarr.d.ts.map +1 -1
  9. package/dist/cli/commands/service.d.ts +1 -2
  10. package/dist/cli/commands/service.d.ts.map +1 -1
  11. package/dist/cli/commands/sonarr.d.ts.map +1 -1
  12. package/dist/cli/config.d.ts +6 -4
  13. package/dist/cli/config.d.ts.map +1 -1
  14. package/dist/cli/index.js +2009 -235
  15. package/dist/clients/bazarr.js +7 -7
  16. package/dist/clients/lidarr.d.ts +79 -1
  17. package/dist/clients/lidarr.d.ts.map +1 -1
  18. package/dist/clients/lidarr.js +100 -7
  19. package/dist/clients/prowlarr.js +7 -7
  20. package/dist/clients/qbittorrent-types.d.ts +2 -0
  21. package/dist/clients/qbittorrent-types.d.ts.map +1 -0
  22. package/dist/clients/qbittorrent.d.ts +40 -0
  23. package/dist/clients/qbittorrent.d.ts.map +1 -0
  24. package/dist/clients/qbittorrent.js +1011 -0
  25. package/dist/clients/radarr.d.ts +92 -1
  26. package/dist/clients/radarr.d.ts.map +1 -1
  27. package/dist/clients/radarr.js +112 -7
  28. package/dist/clients/readarr.d.ts +79 -1
  29. package/dist/clients/readarr.d.ts.map +1 -1
  30. package/dist/clients/readarr.js +100 -7
  31. package/dist/clients/sonarr.d.ts +92 -1
  32. package/dist/clients/sonarr.d.ts.map +1 -1
  33. package/dist/clients/sonarr.js +112 -7
  34. package/dist/core/types.d.ts +6 -0
  35. package/dist/core/types.d.ts.map +1 -1
  36. package/dist/generated/bazarr/client/client.gen.d.ts.map +1 -1
  37. package/dist/generated/bazarr/client/types.gen.d.ts +1 -1
  38. package/dist/generated/bazarr/core/serverSentEvents.gen.d.ts +1 -1
  39. package/dist/generated/bazarr/core/serverSentEvents.gen.d.ts.map +1 -1
  40. package/dist/generated/bazarr/core/types.gen.d.ts +1 -1
  41. package/dist/generated/bazarr/sdk.gen.d.ts +1 -1
  42. package/dist/generated/bazarr/sdk.gen.d.ts.map +1 -1
  43. package/dist/generated/lidarr/client/client.gen.d.ts.map +1 -1
  44. package/dist/generated/lidarr/client/types.gen.d.ts +1 -1
  45. package/dist/generated/lidarr/core/serverSentEvents.gen.d.ts +1 -1
  46. package/dist/generated/lidarr/core/serverSentEvents.gen.d.ts.map +1 -1
  47. package/dist/generated/lidarr/core/types.gen.d.ts +1 -1
  48. package/dist/generated/lidarr/sdk.gen.d.ts +1 -1
  49. package/dist/generated/lidarr/sdk.gen.d.ts.map +1 -1
  50. package/dist/generated/prowlarr/client/client.gen.d.ts.map +1 -1
  51. package/dist/generated/prowlarr/client/types.gen.d.ts +1 -1
  52. package/dist/generated/prowlarr/core/serverSentEvents.gen.d.ts +1 -1
  53. package/dist/generated/prowlarr/core/serverSentEvents.gen.d.ts.map +1 -1
  54. package/dist/generated/prowlarr/core/types.gen.d.ts +1 -1
  55. package/dist/generated/prowlarr/sdk.gen.d.ts +1 -1
  56. package/dist/generated/prowlarr/sdk.gen.d.ts.map +1 -1
  57. package/dist/generated/qbittorrent/client/client.gen.d.ts +3 -0
  58. package/dist/generated/qbittorrent/client/client.gen.d.ts.map +1 -0
  59. package/dist/generated/qbittorrent/client/index.d.ts +9 -0
  60. package/dist/generated/qbittorrent/client/index.d.ts.map +1 -0
  61. package/dist/generated/qbittorrent/client/types.gen.d.ts +118 -0
  62. package/dist/generated/qbittorrent/client/types.gen.d.ts.map +1 -0
  63. package/dist/generated/qbittorrent/client/utils.gen.d.ts +34 -0
  64. package/dist/generated/qbittorrent/client/utils.gen.d.ts.map +1 -0
  65. package/dist/generated/qbittorrent/client.gen.d.ts +13 -0
  66. package/dist/generated/qbittorrent/client.gen.d.ts.map +1 -0
  67. package/dist/generated/qbittorrent/core/auth.gen.d.ts +19 -0
  68. package/dist/generated/qbittorrent/core/auth.gen.d.ts.map +1 -0
  69. package/dist/generated/qbittorrent/core/bodySerializer.gen.d.ts +26 -0
  70. package/dist/generated/qbittorrent/core/bodySerializer.gen.d.ts.map +1 -0
  71. package/dist/generated/qbittorrent/core/params.gen.d.ts +44 -0
  72. package/dist/generated/qbittorrent/core/params.gen.d.ts.map +1 -0
  73. package/dist/generated/qbittorrent/core/pathSerializer.gen.d.ts +34 -0
  74. package/dist/generated/qbittorrent/core/pathSerializer.gen.d.ts.map +1 -0
  75. package/dist/generated/qbittorrent/core/queryKeySerializer.gen.d.ts +19 -0
  76. package/dist/generated/qbittorrent/core/queryKeySerializer.gen.d.ts.map +1 -0
  77. package/dist/generated/qbittorrent/core/serverSentEvents.gen.d.ts +72 -0
  78. package/dist/generated/qbittorrent/core/serverSentEvents.gen.d.ts.map +1 -0
  79. package/dist/generated/qbittorrent/core/types.gen.d.ts +79 -0
  80. package/dist/generated/qbittorrent/core/types.gen.d.ts.map +1 -0
  81. package/dist/generated/qbittorrent/core/utils.gen.d.ts +20 -0
  82. package/dist/generated/qbittorrent/core/utils.gen.d.ts.map +1 -0
  83. package/dist/generated/qbittorrent/index.d.ts +3 -0
  84. package/dist/generated/qbittorrent/index.d.ts.map +1 -0
  85. package/dist/generated/qbittorrent/sdk.gen.d.ts +453 -0
  86. package/dist/generated/qbittorrent/sdk.gen.d.ts.map +1 -0
  87. package/dist/generated/qbittorrent/types.gen.d.ts +3689 -0
  88. package/dist/generated/qbittorrent/types.gen.d.ts.map +1 -0
  89. package/dist/generated/radarr/client/client.gen.d.ts.map +1 -1
  90. package/dist/generated/radarr/client/types.gen.d.ts +1 -1
  91. package/dist/generated/radarr/core/serverSentEvents.gen.d.ts +1 -1
  92. package/dist/generated/radarr/core/serverSentEvents.gen.d.ts.map +1 -1
  93. package/dist/generated/radarr/core/types.gen.d.ts +1 -1
  94. package/dist/generated/radarr/sdk.gen.d.ts +1 -1
  95. package/dist/generated/radarr/sdk.gen.d.ts.map +1 -1
  96. package/dist/generated/readarr/client/client.gen.d.ts.map +1 -1
  97. package/dist/generated/readarr/client/types.gen.d.ts +1 -1
  98. package/dist/generated/readarr/core/serverSentEvents.gen.d.ts +1 -1
  99. package/dist/generated/readarr/core/serverSentEvents.gen.d.ts.map +1 -1
  100. package/dist/generated/readarr/core/types.gen.d.ts +1 -1
  101. package/dist/generated/readarr/sdk.gen.d.ts +1 -1
  102. package/dist/generated/readarr/sdk.gen.d.ts.map +1 -1
  103. package/dist/generated/sonarr/client/client.gen.d.ts.map +1 -1
  104. package/dist/generated/sonarr/client/types.gen.d.ts +1 -1
  105. package/dist/generated/sonarr/core/serverSentEvents.gen.d.ts +1 -1
  106. package/dist/generated/sonarr/core/serverSentEvents.gen.d.ts.map +1 -1
  107. package/dist/generated/sonarr/core/types.gen.d.ts +1 -1
  108. package/dist/generated/sonarr/sdk.gen.d.ts +1 -1
  109. package/dist/generated/sonarr/sdk.gen.d.ts.map +1 -1
  110. package/dist/index.d.ts +2 -0
  111. package/dist/index.d.ts.map +1 -1
  112. package/dist/index.js +30 -31
  113. package/dist/tsarr-2.6.0.tgz +0 -0
  114. package/package.json +17 -9
  115. package/dist/tsarr-2.4.12.tgz +0 -0
package/dist/cli/index.js CHANGED
@@ -75,6 +75,9 @@ function camelCase(str, opts) {
75
75
  function kebabCase(str, joiner) {
76
76
  return str ? (Array.isArray(str) ? str : splitByCase(str)).map((p) => p.toLowerCase()).join(joiner ?? "-") : "";
77
77
  }
78
+ function snakeCase(str) {
79
+ return kebabCase(str || "", "_");
80
+ }
78
81
  var NUMBER_CHAR_RE, STR_SPLITTERS;
79
82
  var init_scule = __esm(() => {
80
83
  NUMBER_CHAR_RE = /\d/;
@@ -134,6 +137,15 @@ function parseRawArgs(args = [], opts = {}) {
134
137
  return "boolean";
135
138
  return "string";
136
139
  }
140
+ function isStringType(name) {
141
+ if (strings.has(name))
142
+ return true;
143
+ const aliases = mainToAliases.get(name) || [];
144
+ for (const alias of aliases)
145
+ if (strings.has(alias))
146
+ return true;
147
+ return false;
148
+ }
137
149
  const allOptions = new Set([
138
150
  ...booleans,
139
151
  ...strings,
@@ -181,8 +193,14 @@ function parseRawArgs(args = [], opts = {}) {
181
193
  }
182
194
  const out = { _: [] };
183
195
  out._ = parsed.positionals;
184
- for (const [key, value] of Object.entries(parsed.values))
185
- out[key] = value;
196
+ for (const [key, value] of Object.entries(parsed.values)) {
197
+ let coerced = value;
198
+ if (getType(key) === "boolean" && typeof value === "string")
199
+ coerced = value !== "false";
200
+ else if (isStringType(key) && typeof value === "boolean")
201
+ coerced = "";
202
+ out[key] = coerced;
203
+ }
186
204
  for (const [name] of Object.entries(negatedFlags)) {
187
205
  out[name] = false;
188
206
  const mainName = aliasToMain.get(name);
@@ -198,6 +216,8 @@ function parseRawArgs(args = [], opts = {}) {
198
216
  out[main] = out[alias];
199
217
  if (out[main] !== undefined && out[alias] === undefined)
200
218
  out[alias] = out[main];
219
+ if (out[alias] !== out[main] && defaults[main] === out[main])
220
+ out[main] = out[alias];
201
221
  }
202
222
  return out;
203
223
  }
@@ -265,6 +285,9 @@ function resolveArgs(argsDef) {
265
285
  });
266
286
  return args;
267
287
  }
288
+ async function resolvePlugins(plugins) {
289
+ return Promise.all(plugins.map((p) => resolveValue(p)));
290
+ }
268
291
  function defineCommand(def) {
269
292
  return def;
270
293
  }
@@ -277,42 +300,112 @@ async function runCommand(cmd, opts) {
277
300
  data: opts.data,
278
301
  cmd
279
302
  };
280
- if (typeof cmd.setup === "function")
281
- await cmd.setup(context);
303
+ const plugins = await resolvePlugins(cmd.plugins ?? []);
282
304
  let result;
305
+ let runError;
283
306
  try {
307
+ for (const plugin of plugins)
308
+ await plugin.setup?.(context);
309
+ if (typeof cmd.setup === "function")
310
+ await cmd.setup(context);
284
311
  const subCommands = await resolveValue(cmd.subCommands);
285
312
  if (subCommands && Object.keys(subCommands).length > 0) {
286
- const subCommandArgIndex = opts.rawArgs.findIndex((arg) => !arg.startsWith("-"));
287
- const subCommandName = opts.rawArgs[subCommandArgIndex];
288
- if (subCommandName) {
289
- if (!subCommands[subCommandName])
290
- throw new CLIError(`Unknown command ${cyan(subCommandName)}`, "E_UNKNOWN_COMMAND");
291
- const subCommand = await resolveValue(subCommands[subCommandName]);
292
- if (subCommand)
293
- await runCommand(subCommand, { rawArgs: opts.rawArgs.slice(subCommandArgIndex + 1) });
294
- } else if (!cmd.run)
295
- throw new CLIError(`No command specified.`, "E_NO_COMMAND");
313
+ const subCommandArgIndex = findSubCommandIndex(opts.rawArgs, cmdArgs);
314
+ const explicitName = opts.rawArgs[subCommandArgIndex];
315
+ if (explicitName) {
316
+ const subCommand = await _findSubCommand(subCommands, explicitName);
317
+ if (!subCommand)
318
+ throw new CLIError(`Unknown command ${cyan(explicitName)}`, "E_UNKNOWN_COMMAND");
319
+ await runCommand(subCommand, { rawArgs: opts.rawArgs.slice(subCommandArgIndex + 1) });
320
+ } else {
321
+ const defaultSubCommand = await resolveValue(cmd.default);
322
+ if (defaultSubCommand) {
323
+ if (cmd.run)
324
+ throw new CLIError(`Cannot specify both 'run' and 'default' on the same command.`, "E_DEFAULT_CONFLICT");
325
+ const subCommand = await _findSubCommand(subCommands, defaultSubCommand);
326
+ if (!subCommand)
327
+ throw new CLIError(`Default sub command ${cyan(defaultSubCommand)} not found in subCommands.`, "E_UNKNOWN_COMMAND");
328
+ await runCommand(subCommand, { rawArgs: opts.rawArgs });
329
+ } else if (!cmd.run)
330
+ throw new CLIError(`No command specified.`, "E_NO_COMMAND");
331
+ }
296
332
  }
297
333
  if (typeof cmd.run === "function")
298
334
  result = await cmd.run(context);
299
- } finally {
300
- if (typeof cmd.cleanup === "function")
301
- await cmd.cleanup(context);
335
+ } catch (error) {
336
+ runError = error;
302
337
  }
338
+ const cleanupErrors = [];
339
+ if (typeof cmd.cleanup === "function")
340
+ try {
341
+ await cmd.cleanup(context);
342
+ } catch (error) {
343
+ cleanupErrors.push(error);
344
+ }
345
+ for (const plugin of [...plugins].reverse())
346
+ try {
347
+ await plugin.cleanup?.(context);
348
+ } catch (error) {
349
+ cleanupErrors.push(error);
350
+ }
351
+ if (runError)
352
+ throw runError;
353
+ if (cleanupErrors.length === 1)
354
+ throw cleanupErrors[0];
355
+ if (cleanupErrors.length > 1)
356
+ throw new Error("Multiple cleanup errors", { cause: cleanupErrors });
303
357
  return { result };
304
358
  }
305
359
  async function resolveSubCommand(cmd, rawArgs, parent) {
306
360
  const subCommands = await resolveValue(cmd.subCommands);
307
361
  if (subCommands && Object.keys(subCommands).length > 0) {
308
- const subCommandArgIndex = rawArgs.findIndex((arg) => !arg.startsWith("-"));
362
+ const subCommandArgIndex = findSubCommandIndex(rawArgs, await resolveValue(cmd.args || {}));
309
363
  const subCommandName = rawArgs[subCommandArgIndex];
310
- const subCommand = await resolveValue(subCommands[subCommandName]);
364
+ const subCommand = await _findSubCommand(subCommands, subCommandName);
311
365
  if (subCommand)
312
366
  return resolveSubCommand(subCommand, rawArgs.slice(subCommandArgIndex + 1), cmd);
313
367
  }
314
368
  return [cmd, parent];
315
369
  }
370
+ async function _findSubCommand(subCommands, name) {
371
+ if (name in subCommands)
372
+ return resolveValue(subCommands[name]);
373
+ for (const sub of Object.values(subCommands)) {
374
+ const resolved = await resolveValue(sub);
375
+ const meta = await resolveValue(resolved?.meta);
376
+ if (meta?.alias) {
377
+ if (toArray(meta.alias).includes(name))
378
+ return resolved;
379
+ }
380
+ }
381
+ }
382
+ function findSubCommandIndex(rawArgs, argsDef) {
383
+ for (let i = 0;i < rawArgs.length; i++) {
384
+ const arg = rawArgs[i];
385
+ if (arg === "--")
386
+ return -1;
387
+ if (arg.startsWith("-")) {
388
+ if (!arg.includes("=") && _isValueFlag(arg, argsDef))
389
+ i++;
390
+ continue;
391
+ }
392
+ return i;
393
+ }
394
+ return -1;
395
+ }
396
+ function _isValueFlag(flag, argsDef) {
397
+ const name = flag.replace(/^-{1,2}/, "");
398
+ const normalized = camelCase(name);
399
+ for (const [key, def] of Object.entries(argsDef)) {
400
+ if (def.type !== "string" && def.type !== "enum")
401
+ continue;
402
+ if (normalized === camelCase(key))
403
+ return true;
404
+ if ((Array.isArray(def.alias) ? def.alias : def.alias ? [def.alias] : []).includes(name))
405
+ return true;
406
+ }
407
+ return false;
408
+ }
316
409
  async function showUsage(cmd, parent) {
317
410
  try {
318
411
  console.log(await renderUsage(cmd, parent) + `
@@ -334,23 +427,18 @@ async function renderUsage(cmd, parent) {
334
427
  if (arg.type === "positional") {
335
428
  const name = arg.name.toUpperCase();
336
429
  const isRequired = arg.required !== false && arg.default === undefined;
337
- const defaultHint = arg.default ? `="${arg.default}"` : "";
338
- posLines.push([
339
- cyan(name + defaultHint),
340
- arg.description || "",
341
- arg.valueHint ? `<${arg.valueHint}>` : ""
342
- ]);
430
+ posLines.push([cyan(name + renderValueHint(arg)), renderDescription(arg, isRequired)]);
343
431
  usageLine.push(isRequired ? `<${name}>` : `[${name}]`);
344
432
  } else {
345
433
  const isRequired = arg.required === true && arg.default === undefined;
346
- const argStr = [...(arg.alias || []).map((a) => `-${a}`), `--${arg.name}`].join(", ") + (arg.type === "string" && (arg.valueHint || arg.default) ? `=${arg.valueHint ? `<${arg.valueHint}>` : `"${arg.default || ""}"`}` : "") + (arg.type === "enum" && arg.options ? `=<${arg.options.join("|")}>` : "");
347
- argLines.push([cyan(argStr + (isRequired ? " (required)" : "")), arg.description || ""]);
434
+ const argStr = [...(arg.alias || []).map((a) => `-${a}`), `--${arg.name}`].join(", ") + renderValueHint(arg);
435
+ argLines.push([cyan(argStr), renderDescription(arg, isRequired)]);
348
436
  if (arg.type === "boolean" && (arg.default === true || arg.negativeDescription) && !negativePrefixRe.test(arg.name)) {
349
437
  const negativeArgStr = [...(arg.alias || []).map((a) => `--no-${a}`), `--no-${arg.name}`].join(", ");
350
- argLines.push([cyan(negativeArgStr + (isRequired ? " (required)" : "")), arg.negativeDescription || ""]);
438
+ argLines.push([cyan(negativeArgStr), [arg.negativeDescription, isRequired ? gray("(Required)") : ""].filter(Boolean).join(" ")]);
351
439
  }
352
440
  if (isRequired)
353
- usageLine.push(argStr);
441
+ usageLine.push(`--${arg.name}` + renderValueHint(arg));
354
442
  }
355
443
  if (cmd.subCommands) {
356
444
  const commandNames = [];
@@ -359,8 +447,10 @@ async function renderUsage(cmd, parent) {
359
447
  const meta = await resolveValue((await resolveValue(sub))?.meta);
360
448
  if (meta?.hidden)
361
449
  continue;
362
- commandsLines.push([cyan(name), meta?.description || ""]);
363
- commandNames.push(name);
450
+ const aliases = toArray(meta?.alias);
451
+ const label = [name, ...aliases].join(", ");
452
+ commandsLines.push([cyan(label), meta?.description || ""]);
453
+ commandNames.push(name, ...aliases);
364
454
  }
365
455
  usageLine.push(commandNames.join("|"));
366
456
  }
@@ -387,14 +477,33 @@ async function renderUsage(cmd, parent) {
387
477
  return usageLines.filter((l) => typeof l === "string").join(`
388
478
  `);
389
479
  }
480
+ function renderValueHint(arg) {
481
+ const valueHint = arg.valueHint ? `=<${arg.valueHint}>` : "";
482
+ const fallbackValueHint = valueHint || `=<${snakeCase(arg.name)}>`;
483
+ if (!arg.type || arg.type === "positional" || arg.type === "boolean")
484
+ return valueHint;
485
+ if (arg.type === "enum" && arg.options?.length)
486
+ return `=<${arg.options.join("|")}>`;
487
+ return fallbackValueHint;
488
+ }
489
+ function renderDescription(arg, required) {
490
+ const requiredHint = required ? gray("(Required)") : "";
491
+ const defaultHint = arg.default === undefined ? "" : gray(`(Default: ${arg.default})`);
492
+ return [
493
+ arg.description,
494
+ requiredHint,
495
+ defaultHint
496
+ ].filter(Boolean).join(" ");
497
+ }
390
498
  async function runMain(cmd, opts = {}) {
391
499
  const rawArgs = opts.rawArgs || process.argv.slice(2);
392
500
  const showUsage$1 = opts.showUsage || showUsage;
393
501
  try {
394
- if (rawArgs.includes("--help") || rawArgs.includes("-h")) {
502
+ const builtinFlags = await _resolveBuiltinFlags(cmd);
503
+ if (builtinFlags.help.length > 0 && rawArgs.some((arg) => builtinFlags.help.includes(arg))) {
395
504
  await showUsage$1(...await resolveSubCommand(cmd, rawArgs));
396
505
  process.exit(0);
397
- } else if (rawArgs.length === 1 && rawArgs[0] === "--version") {
506
+ } else if (rawArgs.length === 1 && builtinFlags.version.includes(rawArgs[0])) {
398
507
  const meta = typeof cmd.meta === "function" ? await cmd.meta() : await cmd.meta;
399
508
  if (!meta?.version)
400
509
  throw new CLIError("No version specified", "E_NO_VERSION");
@@ -411,6 +520,27 @@ async function runMain(cmd, opts = {}) {
411
520
  process.exit(1);
412
521
  }
413
522
  }
523
+ async function _resolveBuiltinFlags(cmd) {
524
+ const argsDef = await resolveValue(cmd.args || {});
525
+ const userNames = /* @__PURE__ */ new Set;
526
+ const userAliases = /* @__PURE__ */ new Set;
527
+ for (const [name, def] of Object.entries(argsDef)) {
528
+ userNames.add(name);
529
+ for (const alias of toArray(def.alias))
530
+ userAliases.add(alias);
531
+ }
532
+ return {
533
+ help: _getBuiltinFlags("help", "h", userNames, userAliases),
534
+ version: _getBuiltinFlags("version", "v", userNames, userAliases)
535
+ };
536
+ }
537
+ function _getBuiltinFlags(long, short, userNames, userAliases) {
538
+ if (userNames.has(long) || userAliases.has(long))
539
+ return [];
540
+ if (userNames.has(short) || userAliases.has(short))
541
+ return [`--${long}`];
542
+ return [`--${long}`, `-${short}`];
543
+ }
414
544
  var CLIError, noColor, _c = (c, r = 39) => (t) => noColor ? t : `\x1B[${c}m${t}\x1B[${r}m`, bold, cyan, gray, underline, negativePrefixRe;
415
545
  var init_dist = __esm(() => {
416
546
  init_scule();
@@ -503,7 +633,7 @@ var init_bodySerializer_gen = __esm(() => {
503
633
  });
504
634
 
505
635
  // src/generated/radarr/core/serverSentEvents.gen.ts
506
- var createSseClient = ({
636
+ function createSseClient({
507
637
  onRequest,
508
638
  onSseError,
509
639
  onSseEvent,
@@ -515,7 +645,7 @@ var createSseClient = ({
515
645
  sseSleepFn,
516
646
  url,
517
647
  ...options
518
- }) => {
648
+ }) {
519
649
  let lastEventId;
520
650
  const sleep = sseSleepFn ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
521
651
  const createStream = async function* () {
@@ -562,8 +692,7 @@ var createSseClient = ({
562
692
  if (done)
563
693
  break;
564
694
  buffer += value;
565
- buffer = buffer.replace(/\r\n/g, `
566
- `).replace(/\r/g, `
695
+ buffer = buffer.replace(/\r\n?/g, `
567
696
  `);
568
697
  const chunks = buffer.split(`
569
698
 
@@ -636,7 +765,7 @@ var createSseClient = ({
636
765
  };
637
766
  const stream = createStream();
638
767
  return { stream };
639
- };
768
+ }
640
769
 
641
770
  // src/generated/radarr/core/pathSerializer.gen.ts
642
771
  var separatorArrayExplode = (style) => {
@@ -1101,8 +1230,9 @@ var createClient = (config = {}) => {
1101
1230
  if (opts.body === undefined || opts.serializedBody === "") {
1102
1231
  opts.headers.delete("Content-Type");
1103
1232
  }
1104
- const url = buildUrl(opts);
1105
- return { opts, url };
1233
+ const resolvedOpts = opts;
1234
+ const url = buildUrl(resolvedOpts);
1235
+ return { opts: resolvedOpts, url };
1106
1236
  };
1107
1237
  const request = async (options) => {
1108
1238
  const { opts, url } = await beforeRequest(options);
@@ -1931,6 +2061,78 @@ var getApiV3SystemBackup = (options) => (options?.client ?? client).get({
1931
2061
  "Content-Type": "application/json",
1932
2062
  ...options.headers
1933
2063
  }
2064
+ }), getApiV3Moviefile = (options) => (options?.client ?? client).get({
2065
+ security: [{ name: "X-Api-Key", type: "apiKey" }, {
2066
+ in: "query",
2067
+ name: "apikey",
2068
+ type: "apiKey"
2069
+ }],
2070
+ url: "/api/v3/moviefile",
2071
+ ...options
2072
+ }), deleteApiV3MoviefileById = (options) => (options.client ?? client).delete({
2073
+ security: [{ name: "X-Api-Key", type: "apiKey" }, {
2074
+ in: "query",
2075
+ name: "apikey",
2076
+ type: "apiKey"
2077
+ }],
2078
+ url: "/api/v3/moviefile/{id}",
2079
+ ...options
2080
+ }), getApiV3MoviefileById = (options) => (options.client ?? client).get({
2081
+ security: [{ name: "X-Api-Key", type: "apiKey" }, {
2082
+ in: "query",
2083
+ name: "apikey",
2084
+ type: "apiKey"
2085
+ }],
2086
+ url: "/api/v3/moviefile/{id}",
2087
+ ...options
2088
+ }), putApiV3MoviefileById = (options) => (options.client ?? client).put({
2089
+ security: [{ name: "X-Api-Key", type: "apiKey" }, {
2090
+ in: "query",
2091
+ name: "apikey",
2092
+ type: "apiKey"
2093
+ }],
2094
+ url: "/api/v3/moviefile/{id}",
2095
+ ...options,
2096
+ headers: {
2097
+ "Content-Type": "application/json",
2098
+ ...options.headers
2099
+ }
2100
+ }), putApiV3MoviefileEditor = (options) => (options?.client ?? client).put({
2101
+ security: [{ name: "X-Api-Key", type: "apiKey" }, {
2102
+ in: "query",
2103
+ name: "apikey",
2104
+ type: "apiKey"
2105
+ }],
2106
+ url: "/api/v3/moviefile/editor",
2107
+ ...options,
2108
+ headers: {
2109
+ "Content-Type": "application/json",
2110
+ ...options?.headers
2111
+ }
2112
+ }), deleteApiV3MoviefileBulk = (options) => (options?.client ?? client).delete({
2113
+ security: [{ name: "X-Api-Key", type: "apiKey" }, {
2114
+ in: "query",
2115
+ name: "apikey",
2116
+ type: "apiKey"
2117
+ }],
2118
+ url: "/api/v3/moviefile/bulk",
2119
+ ...options,
2120
+ headers: {
2121
+ "Content-Type": "application/json",
2122
+ ...options?.headers
2123
+ }
2124
+ }), putApiV3MoviefileBulk = (options) => (options?.client ?? client).put({
2125
+ security: [{ name: "X-Api-Key", type: "apiKey" }, {
2126
+ in: "query",
2127
+ name: "apikey",
2128
+ type: "apiKey"
2129
+ }],
2130
+ url: "/api/v3/moviefile/bulk",
2131
+ ...options,
2132
+ headers: {
2133
+ "Content-Type": "application/json",
2134
+ ...options?.headers
2135
+ }
1934
2136
  }), postApiV3MovieImport = (options) => (options?.client ?? client).post({
1935
2137
  security: [{ name: "X-Api-Key", type: "apiKey" }, {
1936
2138
  in: "query",
@@ -2456,6 +2658,32 @@ class RadarrClient {
2456
2658
  async importMovies(movies) {
2457
2659
  return postApiV3MovieImport({ body: movies });
2458
2660
  }
2661
+ async getMovieFiles(movieId, movieFileIds) {
2662
+ const query = {};
2663
+ if (movieId !== undefined)
2664
+ query.movieId = movieId;
2665
+ if (movieFileIds !== undefined)
2666
+ query.movieFileIds = movieFileIds;
2667
+ return getApiV3Moviefile(Object.keys(query).length > 0 ? { query } : {});
2668
+ }
2669
+ async getMovieFile(id) {
2670
+ return getApiV3MoviefileById({ path: { id } });
2671
+ }
2672
+ async updateMovieFile(id, movieFile) {
2673
+ return putApiV3MoviefileById({ path: { id }, body: movieFile });
2674
+ }
2675
+ async deleteMovieFile(id) {
2676
+ return deleteApiV3MoviefileById({ path: { id } });
2677
+ }
2678
+ async updateMovieFilesEditor(movieFileList) {
2679
+ return putApiV3MoviefileEditor({ body: movieFileList });
2680
+ }
2681
+ async deleteMovieFilesBulk(movieFileList) {
2682
+ return deleteApiV3MoviefileBulk({ body: movieFileList });
2683
+ }
2684
+ async updateMovieFilesBulk(movieFiles) {
2685
+ return putApiV3MoviefileBulk({ body: movieFiles });
2686
+ }
2459
2687
  async getQualityProfiles() {
2460
2688
  return getApiV3Qualityprofile();
2461
2689
  }
@@ -4634,7 +4862,9 @@ function normalizeServiceConfig(service) {
4634
4862
  const normalized = {
4635
4863
  baseUrl: service.baseUrl ?? "",
4636
4864
  apiKey: service.apiKey ?? "",
4637
- ...service.apiKeyFile ? { apiKeyFile: service.apiKeyFile } : {}
4865
+ ...service.apiKeyFile ? { apiKeyFile: service.apiKeyFile } : {},
4866
+ ...service.username ? { username: service.username } : {},
4867
+ ...service.password ? { password: service.password } : {}
4638
4868
  };
4639
4869
  const timeout = typeof service.timeout === "string" ? Number(service.timeout) : service.timeout;
4640
4870
  if (typeof timeout === "number" && Number.isFinite(timeout)) {
@@ -4663,13 +4893,22 @@ function getEnvConfig() {
4663
4893
  for (const service of SERVICES) {
4664
4894
  const upper = service.toUpperCase();
4665
4895
  const baseUrl = process.env[`TSARR_${upper}_URL`];
4666
- const apiKey = process.env[`TSARR_${upper}_API_KEY`];
4667
4896
  const timeout = process.env[`TSARR_${upper}_TIMEOUT`];
4668
4897
  const partial = {};
4669
4898
  if (baseUrl)
4670
4899
  partial.baseUrl = baseUrl;
4671
- if (apiKey)
4672
- partial.apiKey = apiKey;
4900
+ if (service === "qbittorrent") {
4901
+ const username = process.env.TSARR_QBITTORRENT_USERNAME;
4902
+ const password = process.env.TSARR_QBITTORRENT_PASSWORD;
4903
+ if (username)
4904
+ partial.username = username;
4905
+ if (password)
4906
+ partial.password = password;
4907
+ } else {
4908
+ const apiKey = process.env[`TSARR_${upper}_API_KEY`];
4909
+ if (apiKey)
4910
+ partial.apiKey = apiKey;
4911
+ }
4673
4912
  if (timeout)
4674
4913
  partial.timeout = Number(timeout);
4675
4914
  if (Object.keys(partial).length > 0) {
@@ -4752,6 +4991,16 @@ function getServiceConfig(serviceName) {
4752
4991
  const service = config.services[serviceName];
4753
4992
  if (!service?.baseUrl)
4754
4993
  return null;
4994
+ if (serviceName === "qbittorrent") {
4995
+ if (!service.username || !service.password)
4996
+ return null;
4997
+ return {
4998
+ baseUrl: service.baseUrl,
4999
+ username: service.username,
5000
+ password: service.password,
5001
+ ...service.timeout ? { timeout: service.timeout } : {}
5002
+ };
5003
+ }
4755
5004
  let apiKey = service.apiKey;
4756
5005
  const apiKeyFilePath = getResolvedApiKeyFilePath(serviceName, service, localPath, local, global);
4757
5006
  if (!apiKey && apiKeyFilePath) {
@@ -4824,7 +5073,15 @@ function loadScopedConfig(scope) {
4824
5073
  }
4825
5074
  var SERVICES, GLOBAL_CONFIG_DIR, GLOBAL_CONFIG_PATH, LOCAL_CONFIG_NAME = ".tsarr.json";
4826
5075
  var init_config = __esm(() => {
4827
- SERVICES = ["radarr", "sonarr", "lidarr", "readarr", "prowlarr", "bazarr"];
5076
+ SERVICES = [
5077
+ "radarr",
5078
+ "sonarr",
5079
+ "lidarr",
5080
+ "readarr",
5081
+ "prowlarr",
5082
+ "bazarr",
5083
+ "qbittorrent"
5084
+ ];
4828
5085
  GLOBAL_CONFIG_DIR = join(homedir(), ".config", "tsarr");
4829
5086
  GLOBAL_CONFIG_PATH = join(GLOBAL_CONFIG_DIR, "config.json");
4830
5087
  });
@@ -5101,7 +5358,8 @@ function buildServiceCommand(serviceName, description, clientFactory, resources)
5101
5358
  async run({ args }) {
5102
5359
  const config = getServiceConfig(serviceName);
5103
5360
  if (!config) {
5104
- consola.error(`${serviceName} is not configured. Run \`tsarr config init\` or set TSARR_${serviceName.toUpperCase()}_URL and TSARR_${serviceName.toUpperCase()}_API_KEY environment variables.`);
5361
+ const envHint = serviceName === "qbittorrent" ? `TSARR_QBITTORRENT_URL, TSARR_QBITTORRENT_USERNAME, and TSARR_QBITTORRENT_PASSWORD` : `TSARR_${serviceName.toUpperCase()}_URL and TSARR_${serviceName.toUpperCase()}_API_KEY`;
5362
+ consola.error(`${serviceName} is not configured. Run \`tsarr config init\` or set ${envHint} environment variables.`);
5105
5363
  process.exit(1);
5106
5364
  }
5107
5365
  try {
@@ -5143,8 +5401,9 @@ function buildServiceCommand(serviceName, description, clientFactory, resources)
5143
5401
  const err = raw.error;
5144
5402
  const status = err?.status ?? raw?.response?.status;
5145
5403
  if (status === 401) {
5146
- consola.error(`Unauthorized. Check your API key.
5147
- Run \`tsarr config init\` or set TSARR_${serviceName.toUpperCase()}_API_KEY`);
5404
+ const authHint = serviceName === "qbittorrent" ? "Check your username and password." : `Check your API key.
5405
+ Run \`tsarr config init\` or set TSARR_${serviceName.toUpperCase()}_API_KEY`;
5406
+ consola.error(`Unauthorized. ${authHint}`);
5148
5407
  } else if (status === 404) {
5149
5408
  consola.error("Not found.");
5150
5409
  } else {
@@ -5206,6 +5465,8 @@ function isWriteAction(actionName) {
5206
5465
  "create",
5207
5466
  "delete",
5208
5467
  "edit",
5468
+ "pause",
5469
+ "resume",
5209
5470
  "refresh",
5210
5471
  "manual-search",
5211
5472
  "grab",
@@ -5484,6 +5745,32 @@ var init_radarr3 = __esm(() => {
5484
5745
  }
5485
5746
  ]
5486
5747
  },
5748
+ {
5749
+ name: "moviefile",
5750
+ description: "Manage movie files",
5751
+ actions: [
5752
+ {
5753
+ name: "list",
5754
+ description: "List movie files for a movie",
5755
+ args: [{ name: "movie-id", description: "Movie ID", required: true, type: "number" }],
5756
+ columns: ["id", "relativePath", "size", "quality", "dateAdded"],
5757
+ run: (c3, a2) => c3.getMovieFiles([a2["movie-id"]])
5758
+ },
5759
+ {
5760
+ name: "get",
5761
+ description: "Get a movie file by ID",
5762
+ args: [{ name: "id", description: "Movie file ID", required: true, type: "number" }],
5763
+ run: (c3, a2) => c3.getMovieFile(a2.id)
5764
+ },
5765
+ {
5766
+ name: "delete",
5767
+ description: "Delete a movie file from disk",
5768
+ args: [{ name: "id", description: "Movie file ID", required: true, type: "number" }],
5769
+ confirmMessage: "Are you sure you want to delete this movie file from disk?",
5770
+ run: (c3, a2) => c3.deleteMovieFile(a2.id)
5771
+ }
5772
+ ]
5773
+ },
5487
5774
  {
5488
5775
  name: "profile",
5489
5776
  description: "Manage quality profiles",
@@ -5867,7 +6154,7 @@ var init_bodySerializer_gen2 = __esm(() => {
5867
6154
  });
5868
6155
 
5869
6156
  // src/generated/sonarr/core/serverSentEvents.gen.ts
5870
- var createSseClient2 = ({
6157
+ function createSseClient2({
5871
6158
  onRequest,
5872
6159
  onSseError,
5873
6160
  onSseEvent,
@@ -5879,7 +6166,7 @@ var createSseClient2 = ({
5879
6166
  sseSleepFn,
5880
6167
  url,
5881
6168
  ...options
5882
- }) => {
6169
+ }) {
5883
6170
  let lastEventId;
5884
6171
  const sleep = sseSleepFn ?? ((ms) => new Promise((resolve2) => setTimeout(resolve2, ms)));
5885
6172
  const createStream = async function* () {
@@ -5926,8 +6213,7 @@ var createSseClient2 = ({
5926
6213
  if (done)
5927
6214
  break;
5928
6215
  buffer += value;
5929
- buffer = buffer.replace(/\r\n/g, `
5930
- `).replace(/\r/g, `
6216
+ buffer = buffer.replace(/\r\n?/g, `
5931
6217
  `);
5932
6218
  const chunks = buffer.split(`
5933
6219
 
@@ -6000,7 +6286,7 @@ var createSseClient2 = ({
6000
6286
  };
6001
6287
  const stream = createStream();
6002
6288
  return { stream };
6003
- };
6289
+ }
6004
6290
 
6005
6291
  // src/generated/sonarr/core/pathSerializer.gen.ts
6006
6292
  var separatorArrayExplode2 = (style) => {
@@ -6465,8 +6751,9 @@ var createClient2 = (config = {}) => {
6465
6751
  if (opts.body === undefined || opts.serializedBody === "") {
6466
6752
  opts.headers.delete("Content-Type");
6467
6753
  }
6468
- const url = buildUrl2(opts);
6469
- return { opts, url };
6754
+ const resolvedOpts = opts;
6755
+ const url = buildUrl2(resolvedOpts);
6756
+ return { opts: resolvedOpts, url };
6470
6757
  };
6471
6758
  const request = async (options) => {
6472
6759
  const { opts, url } = await beforeRequest(options);
@@ -6995,6 +7282,78 @@ var getApi2 = (options) => (options?.client ?? client2).get({
6995
7282
  "Content-Type": "application/json",
6996
7283
  ...options.headers
6997
7284
  }
7285
+ }), getApiV3Episodefile = (options) => (options?.client ?? client2).get({
7286
+ security: [{ name: "X-Api-Key", type: "apiKey" }, {
7287
+ in: "query",
7288
+ name: "apikey",
7289
+ type: "apiKey"
7290
+ }],
7291
+ url: "/api/v3/episodefile",
7292
+ ...options
7293
+ }), deleteApiV3EpisodefileById = (options) => (options.client ?? client2).delete({
7294
+ security: [{ name: "X-Api-Key", type: "apiKey" }, {
7295
+ in: "query",
7296
+ name: "apikey",
7297
+ type: "apiKey"
7298
+ }],
7299
+ url: "/api/v3/episodefile/{id}",
7300
+ ...options
7301
+ }), getApiV3EpisodefileById = (options) => (options.client ?? client2).get({
7302
+ security: [{ name: "X-Api-Key", type: "apiKey" }, {
7303
+ in: "query",
7304
+ name: "apikey",
7305
+ type: "apiKey"
7306
+ }],
7307
+ url: "/api/v3/episodefile/{id}",
7308
+ ...options
7309
+ }), putApiV3EpisodefileById = (options) => (options.client ?? client2).put({
7310
+ security: [{ name: "X-Api-Key", type: "apiKey" }, {
7311
+ in: "query",
7312
+ name: "apikey",
7313
+ type: "apiKey"
7314
+ }],
7315
+ url: "/api/v3/episodefile/{id}",
7316
+ ...options,
7317
+ headers: {
7318
+ "Content-Type": "application/json",
7319
+ ...options.headers
7320
+ }
7321
+ }), putApiV3EpisodefileEditor = (options) => (options?.client ?? client2).put({
7322
+ security: [{ name: "X-Api-Key", type: "apiKey" }, {
7323
+ in: "query",
7324
+ name: "apikey",
7325
+ type: "apiKey"
7326
+ }],
7327
+ url: "/api/v3/episodefile/editor",
7328
+ ...options,
7329
+ headers: {
7330
+ "Content-Type": "application/json",
7331
+ ...options?.headers
7332
+ }
7333
+ }), deleteApiV3EpisodefileBulk = (options) => (options?.client ?? client2).delete({
7334
+ security: [{ name: "X-Api-Key", type: "apiKey" }, {
7335
+ in: "query",
7336
+ name: "apikey",
7337
+ type: "apiKey"
7338
+ }],
7339
+ url: "/api/v3/episodefile/bulk",
7340
+ ...options,
7341
+ headers: {
7342
+ "Content-Type": "application/json",
7343
+ ...options?.headers
7344
+ }
7345
+ }), putApiV3EpisodefileBulk = (options) => (options?.client ?? client2).put({
7346
+ security: [{ name: "X-Api-Key", type: "apiKey" }, {
7347
+ in: "query",
7348
+ name: "apikey",
7349
+ type: "apiKey"
7350
+ }],
7351
+ url: "/api/v3/episodefile/bulk",
7352
+ ...options,
7353
+ headers: {
7354
+ "Content-Type": "application/json",
7355
+ ...options?.headers
7356
+ }
6998
7357
  }), getApiV3History2 = (options) => (options?.client ?? client2).get({
6999
7358
  security: [{ name: "X-Api-Key", type: "apiKey" }, {
7000
7359
  in: "query",
@@ -7914,6 +8273,32 @@ class SonarrClient {
7914
8273
  async updateEpisode(id, episode) {
7915
8274
  return putApiV3EpisodeById({ path: { id }, body: episode });
7916
8275
  }
8276
+ async getEpisodeFiles(seriesId, episodeFileIds) {
8277
+ const query = {};
8278
+ if (seriesId !== undefined)
8279
+ query.seriesId = seriesId;
8280
+ if (episodeFileIds !== undefined)
8281
+ query.episodeFileIds = episodeFileIds;
8282
+ return getApiV3Episodefile(Object.keys(query).length > 0 ? { query } : {});
8283
+ }
8284
+ async getEpisodeFile(id) {
8285
+ return getApiV3EpisodefileById({ path: { id } });
8286
+ }
8287
+ async updateEpisodeFile(id, episodeFile) {
8288
+ return putApiV3EpisodefileById({ path: { id }, body: episodeFile });
8289
+ }
8290
+ async deleteEpisodeFile(id) {
8291
+ return deleteApiV3EpisodefileById({ path: { id } });
8292
+ }
8293
+ async updateEpisodeFilesEditor(episodeFileList) {
8294
+ return putApiV3EpisodefileEditor({ body: episodeFileList });
8295
+ }
8296
+ async deleteEpisodeFilesBulk(episodeFileList) {
8297
+ return deleteApiV3EpisodefileBulk({ body: episodeFileList });
8298
+ }
8299
+ async updateEpisodeFilesBulk(episodeFiles) {
8300
+ return putApiV3EpisodefileBulk({ body: episodeFiles });
8301
+ }
7917
8302
  async getQualityProfiles() {
7918
8303
  return getApiV3Qualityprofile2();
7919
8304
  }
@@ -8538,6 +8923,32 @@ var init_sonarr3 = __esm(() => {
8538
8923
  }
8539
8924
  ]
8540
8925
  },
8926
+ {
8927
+ name: "episodefile",
8928
+ description: "Manage episode files",
8929
+ actions: [
8930
+ {
8931
+ name: "list",
8932
+ description: "List episode files for a series",
8933
+ args: [{ name: "series-id", description: "Series ID", required: true, type: "number" }],
8934
+ columns: ["id", "relativePath", "size", "quality", "dateAdded"],
8935
+ run: (c3, a2) => c3.getEpisodeFiles(a2["series-id"])
8936
+ },
8937
+ {
8938
+ name: "get",
8939
+ description: "Get an episode file by ID",
8940
+ args: [{ name: "id", description: "Episode file ID", required: true, type: "number" }],
8941
+ run: (c3, a2) => c3.getEpisodeFile(a2.id)
8942
+ },
8943
+ {
8944
+ name: "delete",
8945
+ description: "Delete an episode file from disk",
8946
+ args: [{ name: "id", description: "Episode file ID", required: true, type: "number" }],
8947
+ confirmMessage: "Are you sure you want to delete this episode file from disk?",
8948
+ run: (c3, a2) => c3.deleteEpisodeFile(a2.id)
8949
+ }
8950
+ ]
8951
+ },
8541
8952
  {
8542
8953
  name: "profile",
8543
8954
  description: "Manage quality profiles",
@@ -8918,7 +9329,7 @@ var init_bodySerializer_gen3 = __esm(() => {
8918
9329
  });
8919
9330
 
8920
9331
  // src/generated/lidarr/core/serverSentEvents.gen.ts
8921
- var createSseClient3 = ({
9332
+ function createSseClient3({
8922
9333
  onRequest,
8923
9334
  onSseError,
8924
9335
  onSseEvent,
@@ -8930,7 +9341,7 @@ var createSseClient3 = ({
8930
9341
  sseSleepFn,
8931
9342
  url,
8932
9343
  ...options
8933
- }) => {
9344
+ }) {
8934
9345
  let lastEventId;
8935
9346
  const sleep = sseSleepFn ?? ((ms) => new Promise((resolve2) => setTimeout(resolve2, ms)));
8936
9347
  const createStream = async function* () {
@@ -8977,8 +9388,7 @@ var createSseClient3 = ({
8977
9388
  if (done)
8978
9389
  break;
8979
9390
  buffer += value;
8980
- buffer = buffer.replace(/\r\n/g, `
8981
- `).replace(/\r/g, `
9391
+ buffer = buffer.replace(/\r\n?/g, `
8982
9392
  `);
8983
9393
  const chunks = buffer.split(`
8984
9394
 
@@ -9051,7 +9461,7 @@ var createSseClient3 = ({
9051
9461
  };
9052
9462
  const stream = createStream();
9053
9463
  return { stream };
9054
- };
9464
+ }
9055
9465
 
9056
9466
  // src/generated/lidarr/core/pathSerializer.gen.ts
9057
9467
  var separatorArrayExplode3 = (style) => {
@@ -9516,8 +9926,9 @@ var createClient3 = (config = {}) => {
9516
9926
  if (opts.body === undefined || opts.serializedBody === "") {
9517
9927
  opts.headers.delete("Content-Type");
9518
9928
  }
9519
- const url = buildUrl3(opts);
9520
- return { opts, url };
9929
+ const resolvedOpts = opts;
9930
+ const url = buildUrl3(resolvedOpts);
9931
+ return { opts: resolvedOpts, url };
9521
9932
  };
9522
9933
  const request = async (options) => {
9523
9934
  const { opts, url } = await beforeRequest(options);
@@ -10770,53 +11181,113 @@ var getApiV1Album = (options) => (options?.client ?? client3).get({
10770
11181
  }],
10771
11182
  url: "/api/v1/tag/detail",
10772
11183
  ...options
10773
- }), getApiV1ConfigUiById = (options) => (options.client ?? client3).get({
11184
+ }), deleteApiV1TrackfileById = (options) => (options.client ?? client3).delete({
10774
11185
  security: [{ name: "X-Api-Key", type: "apiKey" }, {
10775
11186
  in: "query",
10776
11187
  name: "apikey",
10777
11188
  type: "apiKey"
10778
11189
  }],
10779
- url: "/api/v1/config/ui/{id}",
11190
+ url: "/api/v1/trackfile/{id}",
10780
11191
  ...options
10781
- }), putApiV1ConfigUiById = (options) => (options.client ?? client3).put({
11192
+ }), getApiV1TrackfileById = (options) => (options.client ?? client3).get({
10782
11193
  security: [{ name: "X-Api-Key", type: "apiKey" }, {
10783
11194
  in: "query",
10784
11195
  name: "apikey",
10785
11196
  type: "apiKey"
10786
11197
  }],
10787
- url: "/api/v1/config/ui/{id}",
11198
+ url: "/api/v1/trackfile/{id}",
11199
+ ...options
11200
+ }), putApiV1TrackfileById = (options) => (options.client ?? client3).put({
11201
+ security: [{ name: "X-Api-Key", type: "apiKey" }, {
11202
+ in: "query",
11203
+ name: "apikey",
11204
+ type: "apiKey"
11205
+ }],
11206
+ url: "/api/v1/trackfile/{id}",
10788
11207
  ...options,
10789
11208
  headers: {
10790
11209
  "Content-Type": "application/json",
10791
11210
  ...options.headers
10792
11211
  }
10793
- }), getApiV1ConfigUi = (options) => (options?.client ?? client3).get({
11212
+ }), getApiV1Trackfile = (options) => (options?.client ?? client3).get({
10794
11213
  security: [{ name: "X-Api-Key", type: "apiKey" }, {
10795
11214
  in: "query",
10796
11215
  name: "apikey",
10797
11216
  type: "apiKey"
10798
11217
  }],
10799
- url: "/api/v1/config/ui",
11218
+ url: "/api/v1/trackfile",
10800
11219
  ...options
10801
- });
10802
- var init_sdk_gen3 = __esm(() => {
10803
- init_client_gen6();
10804
- });
10805
-
10806
- // src/generated/lidarr/index.ts
10807
- var init_lidarr = __esm(() => {
10808
- init_sdk_gen3();
10809
- });
10810
-
10811
- // src/clients/lidarr.ts
10812
- var exports_lidarr = {};
10813
- __export(exports_lidarr, {
10814
- LidarrClient: () => LidarrClient
10815
- });
10816
-
10817
- class LidarrClient {
10818
- clientConfig;
10819
- constructor(config) {
11220
+ }), putApiV1TrackfileEditor = (options) => (options?.client ?? client3).put({
11221
+ security: [{ name: "X-Api-Key", type: "apiKey" }, {
11222
+ in: "query",
11223
+ name: "apikey",
11224
+ type: "apiKey"
11225
+ }],
11226
+ url: "/api/v1/trackfile/editor",
11227
+ ...options,
11228
+ headers: {
11229
+ "Content-Type": "application/json",
11230
+ ...options?.headers
11231
+ }
11232
+ }), deleteApiV1TrackfileBulk = (options) => (options?.client ?? client3).delete({
11233
+ security: [{ name: "X-Api-Key", type: "apiKey" }, {
11234
+ in: "query",
11235
+ name: "apikey",
11236
+ type: "apiKey"
11237
+ }],
11238
+ url: "/api/v1/trackfile/bulk",
11239
+ ...options,
11240
+ headers: {
11241
+ "Content-Type": "application/json",
11242
+ ...options?.headers
11243
+ }
11244
+ }), getApiV1ConfigUiById = (options) => (options.client ?? client3).get({
11245
+ security: [{ name: "X-Api-Key", type: "apiKey" }, {
11246
+ in: "query",
11247
+ name: "apikey",
11248
+ type: "apiKey"
11249
+ }],
11250
+ url: "/api/v1/config/ui/{id}",
11251
+ ...options
11252
+ }), putApiV1ConfigUiById = (options) => (options.client ?? client3).put({
11253
+ security: [{ name: "X-Api-Key", type: "apiKey" }, {
11254
+ in: "query",
11255
+ name: "apikey",
11256
+ type: "apiKey"
11257
+ }],
11258
+ url: "/api/v1/config/ui/{id}",
11259
+ ...options,
11260
+ headers: {
11261
+ "Content-Type": "application/json",
11262
+ ...options.headers
11263
+ }
11264
+ }), getApiV1ConfigUi = (options) => (options?.client ?? client3).get({
11265
+ security: [{ name: "X-Api-Key", type: "apiKey" }, {
11266
+ in: "query",
11267
+ name: "apikey",
11268
+ type: "apiKey"
11269
+ }],
11270
+ url: "/api/v1/config/ui",
11271
+ ...options
11272
+ });
11273
+ var init_sdk_gen3 = __esm(() => {
11274
+ init_client_gen6();
11275
+ });
11276
+
11277
+ // src/generated/lidarr/index.ts
11278
+ var init_lidarr = __esm(() => {
11279
+ init_sdk_gen3();
11280
+ });
11281
+
11282
+ // src/clients/lidarr.ts
11283
+ var exports_lidarr = {};
11284
+ __export(exports_lidarr, {
11285
+ LidarrClient: () => LidarrClient
11286
+ });
11287
+
11288
+ class LidarrClient {
11289
+ clientConfig;
11290
+ constructor(config) {
10820
11291
  this.clientConfig = createServarrClient(config);
10821
11292
  client3.setConfig({
10822
11293
  baseUrl: this.clientConfig.getBaseUrl(),
@@ -10903,6 +11374,33 @@ class LidarrClient {
10903
11374
  query.tags = tags;
10904
11375
  return getFeedV1CalendarLidarrIcs(Object.keys(query).length > 0 ? { query } : {});
10905
11376
  }
11377
+ async getTrackFiles(artistId, trackFileIds, albumId, unmapped) {
11378
+ const query = {};
11379
+ if (artistId !== undefined)
11380
+ query.artistId = artistId;
11381
+ if (trackFileIds !== undefined)
11382
+ query.trackFileIds = trackFileIds;
11383
+ if (albumId !== undefined)
11384
+ query.albumId = albumId;
11385
+ if (unmapped !== undefined)
11386
+ query.unmapped = unmapped;
11387
+ return getApiV1Trackfile(Object.keys(query).length > 0 ? { query } : {});
11388
+ }
11389
+ async getTrackFile(id) {
11390
+ return getApiV1TrackfileById({ path: { id } });
11391
+ }
11392
+ async updateTrackFile(id, trackFile) {
11393
+ return putApiV1TrackfileById({ path: { id }, body: trackFile });
11394
+ }
11395
+ async deleteTrackFile(id) {
11396
+ return deleteApiV1TrackfileById({ path: { id } });
11397
+ }
11398
+ async updateTrackFilesEditor(trackFileList) {
11399
+ return putApiV1TrackfileEditor({ body: trackFileList });
11400
+ }
11401
+ async deleteTrackFilesBulk(trackFileList) {
11402
+ return deleteApiV1TrackfileBulk({ body: trackFileList });
11403
+ }
10906
11404
  async getQualityProfiles() {
10907
11405
  return getApiV1Qualityprofile();
10908
11406
  }
@@ -11504,6 +12002,32 @@ var init_lidarr3 = __esm(() => {
11504
12002
  }
11505
12003
  ]
11506
12004
  },
12005
+ {
12006
+ name: "trackfile",
12007
+ description: "Manage track files",
12008
+ actions: [
12009
+ {
12010
+ name: "list",
12011
+ description: "List track files for an artist",
12012
+ args: [{ name: "artist-id", description: "Artist ID", required: true, type: "number" }],
12013
+ columns: ["id", "relativePath", "size", "quality", "dateAdded"],
12014
+ run: (c3, a2) => c3.getTrackFiles(a2["artist-id"])
12015
+ },
12016
+ {
12017
+ name: "get",
12018
+ description: "Get a track file by ID",
12019
+ args: [{ name: "id", description: "Track file ID", required: true, type: "number" }],
12020
+ run: (c3, a2) => c3.getTrackFile(a2.id)
12021
+ },
12022
+ {
12023
+ name: "delete",
12024
+ description: "Delete a track file from disk",
12025
+ args: [{ name: "id", description: "Track file ID", required: true, type: "number" }],
12026
+ confirmMessage: "Are you sure you want to delete this track file from disk?",
12027
+ run: (c3, a2) => c3.deleteTrackFile(a2.id)
12028
+ }
12029
+ ]
12030
+ },
11507
12031
  {
11508
12032
  name: "profile",
11509
12033
  description: "Manage quality profiles",
@@ -11859,7 +12383,7 @@ var init_bodySerializer_gen4 = __esm(() => {
11859
12383
  });
11860
12384
 
11861
12385
  // src/generated/readarr/core/serverSentEvents.gen.ts
11862
- var createSseClient4 = ({
12386
+ function createSseClient4({
11863
12387
  onRequest,
11864
12388
  onSseError,
11865
12389
  onSseEvent,
@@ -11871,7 +12395,7 @@ var createSseClient4 = ({
11871
12395
  sseSleepFn,
11872
12396
  url,
11873
12397
  ...options
11874
- }) => {
12398
+ }) {
11875
12399
  let lastEventId;
11876
12400
  const sleep = sseSleepFn ?? ((ms) => new Promise((resolve2) => setTimeout(resolve2, ms)));
11877
12401
  const createStream = async function* () {
@@ -11918,8 +12442,7 @@ var createSseClient4 = ({
11918
12442
  if (done)
11919
12443
  break;
11920
12444
  buffer += value;
11921
- buffer = buffer.replace(/\r\n/g, `
11922
- `).replace(/\r/g, `
12445
+ buffer = buffer.replace(/\r\n?/g, `
11923
12446
  `);
11924
12447
  const chunks = buffer.split(`
11925
12448
 
@@ -11992,7 +12515,7 @@ var createSseClient4 = ({
11992
12515
  };
11993
12516
  const stream = createStream();
11994
12517
  return { stream };
11995
- };
12518
+ }
11996
12519
 
11997
12520
  // src/generated/readarr/core/pathSerializer.gen.ts
11998
12521
  var separatorArrayExplode4 = (style) => {
@@ -12457,8 +12980,9 @@ var createClient4 = (config = {}) => {
12457
12980
  if (opts.body === undefined || opts.serializedBody === "") {
12458
12981
  opts.headers.delete("Content-Type");
12459
12982
  }
12460
- const url = buildUrl4(opts);
12461
- return { opts, url };
12983
+ const resolvedOpts = opts;
12984
+ const url = buildUrl4(resolvedOpts);
12985
+ return { opts: resolvedOpts, url };
12462
12986
  };
12463
12987
  const request = async (options) => {
12464
12988
  const { opts, url } = await beforeRequest(options);
@@ -12815,6 +13339,66 @@ var getApiV1Author = (options) => (options?.client ?? client4).get({
12815
13339
  "Content-Type": "application/json",
12816
13340
  ...options.headers
12817
13341
  }
13342
+ }), getApiV1Bookfile = (options) => (options?.client ?? client4).get({
13343
+ security: [{ name: "X-Api-Key", type: "apiKey" }, {
13344
+ in: "query",
13345
+ name: "apikey",
13346
+ type: "apiKey"
13347
+ }],
13348
+ url: "/api/v1/bookfile",
13349
+ ...options
13350
+ }), deleteApiV1BookfileById = (options) => (options.client ?? client4).delete({
13351
+ security: [{ name: "X-Api-Key", type: "apiKey" }, {
13352
+ in: "query",
13353
+ name: "apikey",
13354
+ type: "apiKey"
13355
+ }],
13356
+ url: "/api/v1/bookfile/{id}",
13357
+ ...options
13358
+ }), getApiV1BookfileById = (options) => (options.client ?? client4).get({
13359
+ security: [{ name: "X-Api-Key", type: "apiKey" }, {
13360
+ in: "query",
13361
+ name: "apikey",
13362
+ type: "apiKey"
13363
+ }],
13364
+ url: "/api/v1/bookfile/{id}",
13365
+ ...options
13366
+ }), putApiV1BookfileById = (options) => (options.client ?? client4).put({
13367
+ security: [{ name: "X-Api-Key", type: "apiKey" }, {
13368
+ in: "query",
13369
+ name: "apikey",
13370
+ type: "apiKey"
13371
+ }],
13372
+ url: "/api/v1/bookfile/{id}",
13373
+ ...options,
13374
+ headers: {
13375
+ "Content-Type": "application/json",
13376
+ ...options.headers
13377
+ }
13378
+ }), putApiV1BookfileEditor = (options) => (options?.client ?? client4).put({
13379
+ security: [{ name: "X-Api-Key", type: "apiKey" }, {
13380
+ in: "query",
13381
+ name: "apikey",
13382
+ type: "apiKey"
13383
+ }],
13384
+ url: "/api/v1/bookfile/editor",
13385
+ ...options,
13386
+ headers: {
13387
+ "Content-Type": "application/json",
13388
+ ...options?.headers
13389
+ }
13390
+ }), deleteApiV1BookfileBulk = (options) => (options?.client ?? client4).delete({
13391
+ security: [{ name: "X-Api-Key", type: "apiKey" }, {
13392
+ in: "query",
13393
+ name: "apikey",
13394
+ type: "apiKey"
13395
+ }],
13396
+ url: "/api/v1/bookfile/bulk",
13397
+ ...options,
13398
+ headers: {
13399
+ "Content-Type": "application/json",
13400
+ ...options?.headers
13401
+ }
12818
13402
  }), getApiV1BookLookup = (options) => (options?.client ?? client4).get({
12819
13403
  security: [{ name: "X-Api-Key", type: "apiKey" }, {
12820
13404
  in: "query",
@@ -13934,6 +14518,33 @@ class ReadarrClient {
13934
14518
  query.tagList = tagList;
13935
14519
  return getFeedV1CalendarReadarrIcs(Object.keys(query).length > 0 ? { query } : {});
13936
14520
  }
14521
+ async getBookFiles(authorId, bookFileIds, bookId, unmapped) {
14522
+ const query = {};
14523
+ if (authorId !== undefined)
14524
+ query.authorId = authorId;
14525
+ if (bookFileIds !== undefined)
14526
+ query.bookFileIds = bookFileIds;
14527
+ if (bookId !== undefined)
14528
+ query.bookId = bookId;
14529
+ if (unmapped !== undefined)
14530
+ query.unmapped = unmapped;
14531
+ return getApiV1Bookfile(Object.keys(query).length > 0 ? { query } : {});
14532
+ }
14533
+ async getBookFile(id) {
14534
+ return getApiV1BookfileById({ path: { id } });
14535
+ }
14536
+ async updateBookFile(id, bookFile) {
14537
+ return putApiV1BookfileById({ path: { id }, body: bookFile });
14538
+ }
14539
+ async deleteBookFile(id) {
14540
+ return deleteApiV1BookfileById({ path: { id } });
14541
+ }
14542
+ async updateBookFilesEditor(bookFileList) {
14543
+ return putApiV1BookfileEditor({ body: bookFileList });
14544
+ }
14545
+ async deleteBookFilesBulk(bookFileList) {
14546
+ return deleteApiV1BookfileBulk({ body: bookFileList });
14547
+ }
13937
14548
  async getQualityProfiles() {
13938
14549
  return getApiV1Qualityprofile2();
13939
14550
  }
@@ -14426,6 +15037,32 @@ var init_readarr3 = __esm(() => {
14426
15037
  }
14427
15038
  ]
14428
15039
  },
15040
+ {
15041
+ name: "bookfile",
15042
+ description: "Manage book files",
15043
+ actions: [
15044
+ {
15045
+ name: "list",
15046
+ description: "List book files for an author",
15047
+ args: [{ name: "author-id", description: "Author ID", required: true, type: "number" }],
15048
+ columns: ["id", "relativePath", "size", "quality", "dateAdded"],
15049
+ run: (c3, a2) => c3.getBookFiles(a2["author-id"])
15050
+ },
15051
+ {
15052
+ name: "get",
15053
+ description: "Get a book file by ID",
15054
+ args: [{ name: "id", description: "Book file ID", required: true, type: "number" }],
15055
+ run: (c3, a2) => c3.getBookFile(a2.id)
15056
+ },
15057
+ {
15058
+ name: "delete",
15059
+ description: "Delete a book file from disk",
15060
+ args: [{ name: "id", description: "Book file ID", required: true, type: "number" }],
15061
+ confirmMessage: "Are you sure you want to delete this book file from disk?",
15062
+ run: (c3, a2) => c3.deleteBookFile(a2.id)
15063
+ }
15064
+ ]
15065
+ },
14429
15066
  {
14430
15067
  name: "profile",
14431
15068
  description: "Manage quality profiles",
@@ -14781,7 +15418,7 @@ var init_bodySerializer_gen5 = __esm(() => {
14781
15418
  });
14782
15419
 
14783
15420
  // src/generated/prowlarr/core/serverSentEvents.gen.ts
14784
- var createSseClient5 = ({
15421
+ function createSseClient5({
14785
15422
  onRequest,
14786
15423
  onSseError,
14787
15424
  onSseEvent,
@@ -14793,7 +15430,7 @@ var createSseClient5 = ({
14793
15430
  sseSleepFn,
14794
15431
  url,
14795
15432
  ...options
14796
- }) => {
15433
+ }) {
14797
15434
  let lastEventId;
14798
15435
  const sleep = sseSleepFn ?? ((ms) => new Promise((resolve2) => setTimeout(resolve2, ms)));
14799
15436
  const createStream = async function* () {
@@ -14840,8 +15477,7 @@ var createSseClient5 = ({
14840
15477
  if (done)
14841
15478
  break;
14842
15479
  buffer += value;
14843
- buffer = buffer.replace(/\r\n/g, `
14844
- `).replace(/\r/g, `
15480
+ buffer = buffer.replace(/\r\n?/g, `
14845
15481
  `);
14846
15482
  const chunks = buffer.split(`
14847
15483
 
@@ -14914,7 +15550,7 @@ var createSseClient5 = ({
14914
15550
  };
14915
15551
  const stream = createStream();
14916
15552
  return { stream };
14917
- };
15553
+ }
14918
15554
 
14919
15555
  // src/generated/prowlarr/core/pathSerializer.gen.ts
14920
15556
  var separatorArrayExplode5 = (style) => {
@@ -15379,8 +16015,9 @@ var createClient5 = (config = {}) => {
15379
16015
  if (opts.body === undefined || opts.serializedBody === "") {
15380
16016
  opts.headers.delete("Content-Type");
15381
16017
  }
15382
- const url = buildUrl5(opts);
15383
- return { opts, url };
16018
+ const resolvedOpts = opts;
16019
+ const url = buildUrl5(resolvedOpts);
16020
+ return { opts: resolvedOpts, url };
15384
16021
  };
15385
16022
  const request = async (options) => {
15386
16023
  const { opts, url } = await beforeRequest(options);
@@ -16780,7 +17417,7 @@ var init_bodySerializer_gen6 = __esm(() => {
16780
17417
  });
16781
17418
 
16782
17419
  // src/generated/bazarr/core/serverSentEvents.gen.ts
16783
- var createSseClient6 = ({
17420
+ function createSseClient6({
16784
17421
  onRequest,
16785
17422
  onSseError,
16786
17423
  onSseEvent,
@@ -16792,7 +17429,7 @@ var createSseClient6 = ({
16792
17429
  sseSleepFn,
16793
17430
  url,
16794
17431
  ...options
16795
- }) => {
17432
+ }) {
16796
17433
  let lastEventId;
16797
17434
  const sleep = sseSleepFn ?? ((ms) => new Promise((resolve2) => setTimeout(resolve2, ms)));
16798
17435
  const createStream = async function* () {
@@ -16839,8 +17476,7 @@ var createSseClient6 = ({
16839
17476
  if (done)
16840
17477
  break;
16841
17478
  buffer += value;
16842
- buffer = buffer.replace(/\r\n/g, `
16843
- `).replace(/\r/g, `
17479
+ buffer = buffer.replace(/\r\n?/g, `
16844
17480
  `);
16845
17481
  const chunks = buffer.split(`
16846
17482
 
@@ -16913,7 +17549,7 @@ var createSseClient6 = ({
16913
17549
  };
16914
17550
  const stream = createStream();
16915
17551
  return { stream };
16916
- };
17552
+ }
16917
17553
 
16918
17554
  // src/generated/bazarr/core/pathSerializer.gen.ts
16919
17555
  var separatorArrayExplode6 = (style) => {
@@ -17378,8 +18014,9 @@ var createClient6 = (config = {}) => {
17378
18014
  if (opts.body === undefined || opts.serializedBody === "") {
17379
18015
  opts.headers.delete("Content-Type");
17380
18016
  }
17381
- const url = buildUrl6(opts);
17382
- return { opts, url };
18017
+ const resolvedOpts = opts;
18018
+ const url = buildUrl6(resolvedOpts);
18019
+ return { opts: resolvedOpts, url };
17383
18020
  };
17384
18021
  const request = async (options) => {
17385
18022
  const { opts, url } = await beforeRequest(options);
@@ -18320,90 +18957,1202 @@ var init_bazarr3 = __esm(() => {
18320
18957
  bazarr = buildServiceCommand("bazarr", "Manage Bazarr (Subtitles)", (config) => new BazarrClient(config), resources6);
18321
18958
  });
18322
18959
 
18323
- // src/cli/commands/doctor.ts
18324
- var exports_doctor = {};
18325
- __export(exports_doctor, {
18326
- doctor: () => doctor
18327
- });
18328
- function classifyError(error) {
18329
- if (!(error instanceof Error))
18330
- return "Unknown error";
18331
- const msg = error.message;
18332
- const cause = error.cause;
18333
- if (cause?.code === "ECONNREFUSED" || msg.includes("ECONNREFUSED")) {
18334
- return "Connection refused - is the service running?";
18335
- }
18336
- if (cause?.code === "ENOTFOUND" || msg.includes("ENOTFOUND")) {
18337
- return "Host not found - check the URL";
18338
- }
18339
- if (cause?.code === "ECONNRESET" || msg.includes("ECONNRESET")) {
18340
- return "Connection reset - service may have crashed";
18341
- }
18342
- if (cause?.code === "ETIMEDOUT" || msg.includes("ETIMEDOUT") || msg.includes("timed out")) {
18343
- return "Connection timed out - service may be unreachable";
18344
- }
18345
- if (msg.includes("fetch failed") || msg.includes("Failed to fetch")) {
18346
- return `Service unreachable - ${cause?.message ?? "check URL and network"}`;
18347
- }
18348
- if (msg.includes("401") || msg.includes("Unauthorized")) {
18349
- return "Authentication failed (401) - check your API key";
18350
- }
18351
- if (msg.includes("403") || msg.includes("Forbidden")) {
18352
- return "Access denied (403) - check your API key permissions";
18353
- }
18354
- if (msg.includes("502") || msg.includes("Bad Gateway")) {
18355
- return "Bad gateway (502) - reverse proxy or service issue";
18356
- }
18357
- if (msg.includes("503") || msg.includes("Service Unavailable")) {
18358
- return "Service unavailable (503) - service may be starting up";
18359
- }
18360
- if (msg.includes("CERT") || msg.includes("certificate") || msg.includes("SSL")) {
18361
- return "SSL/TLS certificate error - check HTTPS configuration";
18362
- }
18363
- return msg;
18364
- }
18365
- function extractVersion(service, status) {
18366
- const data = status?.data ?? status;
18367
- if (typeof data === "string") {
18368
- return null;
18369
- }
18370
- if (service === "bazarr") {
18371
- return data?.data?.bazarr_version ?? data?.bazarr_version ?? null;
18960
+ // src/generated/qbittorrent/core/bodySerializer.gen.ts
18961
+ var serializeUrlSearchParamsPair = (data, key, value) => {
18962
+ if (typeof value === "string") {
18963
+ data.append(key, value);
18964
+ } else {
18965
+ data.append(key, JSON.stringify(value));
18372
18966
  }
18373
- return data?.version ?? status?.version ?? null;
18374
- }
18375
- var clientFactories, doctor;
18376
- var init_doctor = __esm(() => {
18377
- init_dist();
18378
- init_dist2();
18379
- init_bazarr2();
18380
- init_lidarr2();
18381
- init_prowlarr2();
18382
- init_radarr2();
18383
- init_readarr2();
18384
- init_sonarr2();
18385
- init_config();
18386
- init_output();
18387
- clientFactories = {
18388
- radarr: (c3) => new RadarrClient(c3),
18389
- sonarr: (c3) => new SonarrClient(c3),
18390
- lidarr: (c3) => new LidarrClient(c3),
18391
- readarr: (c3) => new ReadarrClient(c3),
18392
- prowlarr: (c3) => new ProwlarrClient(c3),
18393
- bazarr: (c3) => new BazarrClient(c3)
18967
+ }, jsonBodySerializer7, urlSearchParamsBodySerializer2;
18968
+ var init_bodySerializer_gen7 = __esm(() => {
18969
+ jsonBodySerializer7 = {
18970
+ bodySerializer: (body) => JSON.stringify(body, (_key, value) => typeof value === "bigint" ? value.toString() : value)
18394
18971
  };
18395
- doctor = defineCommand({
18396
- meta: {
18397
- name: "doctor",
18398
- description: "Test all configured service connections"
18399
- },
18400
- args: {
18401
- json: { type: "boolean", description: "Output as JSON" },
18402
- table: { type: "boolean", description: "Output as table" },
18403
- plain: { type: "boolean", description: "Output as TSV (no colors, for piping)" },
18404
- quiet: { type: "boolean", alias: "q", description: "Output service names only" },
18405
- select: { type: "string", description: "Cherry-pick fields (comma-separated, JSON mode)" }
18406
- },
18972
+ urlSearchParamsBodySerializer2 = {
18973
+ bodySerializer: (body) => {
18974
+ const data = new URLSearchParams;
18975
+ Object.entries(body).forEach(([key, value]) => {
18976
+ if (value === undefined || value === null) {
18977
+ return;
18978
+ }
18979
+ if (Array.isArray(value)) {
18980
+ value.forEach((v2) => serializeUrlSearchParamsPair(data, key, v2));
18981
+ } else {
18982
+ serializeUrlSearchParamsPair(data, key, value);
18983
+ }
18984
+ });
18985
+ return data.toString();
18986
+ }
18987
+ };
18988
+ });
18989
+
18990
+ // src/generated/qbittorrent/core/serverSentEvents.gen.ts
18991
+ function createSseClient7({
18992
+ onRequest,
18993
+ onSseError,
18994
+ onSseEvent,
18995
+ responseTransformer,
18996
+ responseValidator,
18997
+ sseDefaultRetryDelay,
18998
+ sseMaxRetryAttempts,
18999
+ sseMaxRetryDelay,
19000
+ sseSleepFn,
19001
+ url,
19002
+ ...options
19003
+ }) {
19004
+ let lastEventId;
19005
+ const sleep = sseSleepFn ?? ((ms) => new Promise((resolve2) => setTimeout(resolve2, ms)));
19006
+ const createStream = async function* () {
19007
+ let retryDelay = sseDefaultRetryDelay ?? 3000;
19008
+ let attempt = 0;
19009
+ const signal = options.signal ?? new AbortController().signal;
19010
+ while (true) {
19011
+ if (signal.aborted)
19012
+ break;
19013
+ attempt++;
19014
+ const headers = options.headers instanceof Headers ? options.headers : new Headers(options.headers);
19015
+ if (lastEventId !== undefined) {
19016
+ headers.set("Last-Event-ID", lastEventId);
19017
+ }
19018
+ try {
19019
+ const requestInit = {
19020
+ redirect: "follow",
19021
+ ...options,
19022
+ body: options.serializedBody,
19023
+ headers,
19024
+ signal
19025
+ };
19026
+ let request = new Request(url, requestInit);
19027
+ if (onRequest) {
19028
+ request = await onRequest(url, requestInit);
19029
+ }
19030
+ const _fetch = options.fetch ?? globalThis.fetch;
19031
+ const response = await _fetch(request);
19032
+ if (!response.ok)
19033
+ throw new Error(`SSE failed: ${response.status} ${response.statusText}`);
19034
+ if (!response.body)
19035
+ throw new Error("No body in SSE response");
19036
+ const reader = response.body.pipeThrough(new TextDecoderStream).getReader();
19037
+ let buffer = "";
19038
+ const abortHandler = () => {
19039
+ try {
19040
+ reader.cancel();
19041
+ } catch {}
19042
+ };
19043
+ signal.addEventListener("abort", abortHandler);
19044
+ try {
19045
+ while (true) {
19046
+ const { done, value } = await reader.read();
19047
+ if (done)
19048
+ break;
19049
+ buffer += value;
19050
+ buffer = buffer.replace(/\r\n?/g, `
19051
+ `);
19052
+ const chunks = buffer.split(`
19053
+
19054
+ `);
19055
+ buffer = chunks.pop() ?? "";
19056
+ for (const chunk of chunks) {
19057
+ const lines = chunk.split(`
19058
+ `);
19059
+ const dataLines = [];
19060
+ let eventName;
19061
+ for (const line of lines) {
19062
+ if (line.startsWith("data:")) {
19063
+ dataLines.push(line.replace(/^data:\s*/, ""));
19064
+ } else if (line.startsWith("event:")) {
19065
+ eventName = line.replace(/^event:\s*/, "");
19066
+ } else if (line.startsWith("id:")) {
19067
+ lastEventId = line.replace(/^id:\s*/, "");
19068
+ } else if (line.startsWith("retry:")) {
19069
+ const parsed = Number.parseInt(line.replace(/^retry:\s*/, ""), 10);
19070
+ if (!Number.isNaN(parsed)) {
19071
+ retryDelay = parsed;
19072
+ }
19073
+ }
19074
+ }
19075
+ let data;
19076
+ let parsedJson = false;
19077
+ if (dataLines.length) {
19078
+ const rawData = dataLines.join(`
19079
+ `);
19080
+ try {
19081
+ data = JSON.parse(rawData);
19082
+ parsedJson = true;
19083
+ } catch {
19084
+ data = rawData;
19085
+ }
19086
+ }
19087
+ if (parsedJson) {
19088
+ if (responseValidator) {
19089
+ await responseValidator(data);
19090
+ }
19091
+ if (responseTransformer) {
19092
+ data = await responseTransformer(data);
19093
+ }
19094
+ }
19095
+ onSseEvent?.({
19096
+ data,
19097
+ event: eventName,
19098
+ id: lastEventId,
19099
+ retry: retryDelay
19100
+ });
19101
+ if (dataLines.length) {
19102
+ yield data;
19103
+ }
19104
+ }
19105
+ }
19106
+ } finally {
19107
+ signal.removeEventListener("abort", abortHandler);
19108
+ reader.releaseLock();
19109
+ }
19110
+ break;
19111
+ } catch (error) {
19112
+ onSseError?.(error);
19113
+ if (sseMaxRetryAttempts !== undefined && attempt >= sseMaxRetryAttempts) {
19114
+ break;
19115
+ }
19116
+ const backoff = Math.min(retryDelay * 2 ** (attempt - 1), sseMaxRetryDelay ?? 30000);
19117
+ await sleep(backoff);
19118
+ }
19119
+ }
19120
+ };
19121
+ const stream = createStream();
19122
+ return { stream };
19123
+ }
19124
+
19125
+ // src/generated/qbittorrent/core/pathSerializer.gen.ts
19126
+ var separatorArrayExplode7 = (style) => {
19127
+ switch (style) {
19128
+ case "label":
19129
+ return ".";
19130
+ case "matrix":
19131
+ return ";";
19132
+ case "simple":
19133
+ return ",";
19134
+ default:
19135
+ return "&";
19136
+ }
19137
+ }, separatorArrayNoExplode7 = (style) => {
19138
+ switch (style) {
19139
+ case "form":
19140
+ return ",";
19141
+ case "pipeDelimited":
19142
+ return "|";
19143
+ case "spaceDelimited":
19144
+ return "%20";
19145
+ default:
19146
+ return ",";
19147
+ }
19148
+ }, separatorObjectExplode7 = (style) => {
19149
+ switch (style) {
19150
+ case "label":
19151
+ return ".";
19152
+ case "matrix":
19153
+ return ";";
19154
+ case "simple":
19155
+ return ",";
19156
+ default:
19157
+ return "&";
19158
+ }
19159
+ }, serializeArrayParam7 = ({
19160
+ allowReserved,
19161
+ explode,
19162
+ name,
19163
+ style,
19164
+ value
19165
+ }) => {
19166
+ if (!explode) {
19167
+ const joinedValues2 = (allowReserved ? value : value.map((v2) => encodeURIComponent(v2))).join(separatorArrayNoExplode7(style));
19168
+ switch (style) {
19169
+ case "label":
19170
+ return `.${joinedValues2}`;
19171
+ case "matrix":
19172
+ return `;${name}=${joinedValues2}`;
19173
+ case "simple":
19174
+ return joinedValues2;
19175
+ default:
19176
+ return `${name}=${joinedValues2}`;
19177
+ }
19178
+ }
19179
+ const separator = separatorArrayExplode7(style);
19180
+ const joinedValues = value.map((v2) => {
19181
+ if (style === "label" || style === "simple") {
19182
+ return allowReserved ? v2 : encodeURIComponent(v2);
19183
+ }
19184
+ return serializePrimitiveParam7({
19185
+ allowReserved,
19186
+ name,
19187
+ value: v2
19188
+ });
19189
+ }).join(separator);
19190
+ return style === "label" || style === "matrix" ? separator + joinedValues : joinedValues;
19191
+ }, serializePrimitiveParam7 = ({
19192
+ allowReserved,
19193
+ name,
19194
+ value
19195
+ }) => {
19196
+ if (value === undefined || value === null) {
19197
+ return "";
19198
+ }
19199
+ if (typeof value === "object") {
19200
+ throw new Error("Deeply-nested arrays/objects aren’t supported. Provide your own `querySerializer()` to handle these.");
19201
+ }
19202
+ return `${name}=${allowReserved ? value : encodeURIComponent(value)}`;
19203
+ }, serializeObjectParam7 = ({
19204
+ allowReserved,
19205
+ explode,
19206
+ name,
19207
+ style,
19208
+ value,
19209
+ valueOnly
19210
+ }) => {
19211
+ if (value instanceof Date) {
19212
+ return valueOnly ? value.toISOString() : `${name}=${value.toISOString()}`;
19213
+ }
19214
+ if (style !== "deepObject" && !explode) {
19215
+ let values = [];
19216
+ Object.entries(value).forEach(([key, v2]) => {
19217
+ values = [...values, key, allowReserved ? v2 : encodeURIComponent(v2)];
19218
+ });
19219
+ const joinedValues2 = values.join(",");
19220
+ switch (style) {
19221
+ case "form":
19222
+ return `${name}=${joinedValues2}`;
19223
+ case "label":
19224
+ return `.${joinedValues2}`;
19225
+ case "matrix":
19226
+ return `;${name}=${joinedValues2}`;
19227
+ default:
19228
+ return joinedValues2;
19229
+ }
19230
+ }
19231
+ const separator = separatorObjectExplode7(style);
19232
+ const joinedValues = Object.entries(value).map(([key, v2]) => serializePrimitiveParam7({
19233
+ allowReserved,
19234
+ name: style === "deepObject" ? `${name}[${key}]` : key,
19235
+ value: v2
19236
+ })).join(separator);
19237
+ return style === "label" || style === "matrix" ? separator + joinedValues : joinedValues;
19238
+ };
19239
+
19240
+ // src/generated/qbittorrent/core/utils.gen.ts
19241
+ function getValidRequestBody7(options) {
19242
+ const hasBody = options.body !== undefined;
19243
+ const isSerializedBody = hasBody && options.bodySerializer;
19244
+ if (isSerializedBody) {
19245
+ if ("serializedBody" in options) {
19246
+ const hasSerializedBody = options.serializedBody !== undefined && options.serializedBody !== "";
19247
+ return hasSerializedBody ? options.serializedBody : null;
19248
+ }
19249
+ return options.body !== "" ? options.body : null;
19250
+ }
19251
+ if (hasBody) {
19252
+ return options.body;
19253
+ }
19254
+ return;
19255
+ }
19256
+ var PATH_PARAM_RE7, defaultPathSerializer7 = ({ path, url: _url }) => {
19257
+ let url = _url;
19258
+ const matches = _url.match(PATH_PARAM_RE7);
19259
+ if (matches) {
19260
+ for (const match of matches) {
19261
+ let explode = false;
19262
+ let name = match.substring(1, match.length - 1);
19263
+ let style = "simple";
19264
+ if (name.endsWith("*")) {
19265
+ explode = true;
19266
+ name = name.substring(0, name.length - 1);
19267
+ }
19268
+ if (name.startsWith(".")) {
19269
+ name = name.substring(1);
19270
+ style = "label";
19271
+ } else if (name.startsWith(";")) {
19272
+ name = name.substring(1);
19273
+ style = "matrix";
19274
+ }
19275
+ const value = path[name];
19276
+ if (value === undefined || value === null) {
19277
+ continue;
19278
+ }
19279
+ if (Array.isArray(value)) {
19280
+ url = url.replace(match, serializeArrayParam7({ explode, name, style, value }));
19281
+ continue;
19282
+ }
19283
+ if (typeof value === "object") {
19284
+ url = url.replace(match, serializeObjectParam7({
19285
+ explode,
19286
+ name,
19287
+ style,
19288
+ value,
19289
+ valueOnly: true
19290
+ }));
19291
+ continue;
19292
+ }
19293
+ if (style === "matrix") {
19294
+ url = url.replace(match, `;${serializePrimitiveParam7({
19295
+ name,
19296
+ value
19297
+ })}`);
19298
+ continue;
19299
+ }
19300
+ const replaceValue = encodeURIComponent(style === "label" ? `.${value}` : value);
19301
+ url = url.replace(match, replaceValue);
19302
+ }
19303
+ }
19304
+ return url;
19305
+ }, getUrl7 = ({
19306
+ baseUrl,
19307
+ path,
19308
+ query,
19309
+ querySerializer,
19310
+ url: _url
19311
+ }) => {
19312
+ const pathUrl = _url.startsWith("/") ? _url : `/${_url}`;
19313
+ let url = (baseUrl ?? "") + pathUrl;
19314
+ if (path) {
19315
+ url = defaultPathSerializer7({ path, url });
19316
+ }
19317
+ let search = query ? querySerializer(query) : "";
19318
+ if (search.startsWith("?")) {
19319
+ search = search.substring(1);
19320
+ }
19321
+ if (search) {
19322
+ url += `?${search}`;
19323
+ }
19324
+ return url;
19325
+ };
19326
+ var init_utils_gen13 = __esm(() => {
19327
+ PATH_PARAM_RE7 = /\{[^{}]+\}/g;
19328
+ });
19329
+
19330
+ // src/generated/qbittorrent/core/auth.gen.ts
19331
+ var getAuthToken7 = async (auth, callback) => {
19332
+ const token = typeof callback === "function" ? await callback(auth) : callback;
19333
+ if (!token) {
19334
+ return;
19335
+ }
19336
+ if (auth.scheme === "bearer") {
19337
+ return `Bearer ${token}`;
19338
+ }
19339
+ if (auth.scheme === "basic") {
19340
+ return `Basic ${btoa(token)}`;
19341
+ }
19342
+ return token;
19343
+ };
19344
+
19345
+ // src/generated/qbittorrent/client/utils.gen.ts
19346
+ class Interceptors7 {
19347
+ fns = [];
19348
+ clear() {
19349
+ this.fns = [];
19350
+ }
19351
+ eject(id) {
19352
+ const index = this.getInterceptorIndex(id);
19353
+ if (this.fns[index]) {
19354
+ this.fns[index] = null;
19355
+ }
19356
+ }
19357
+ exists(id) {
19358
+ const index = this.getInterceptorIndex(id);
19359
+ return Boolean(this.fns[index]);
19360
+ }
19361
+ getInterceptorIndex(id) {
19362
+ if (typeof id === "number") {
19363
+ return this.fns[id] ? id : -1;
19364
+ }
19365
+ return this.fns.indexOf(id);
19366
+ }
19367
+ update(id, fn) {
19368
+ const index = this.getInterceptorIndex(id);
19369
+ if (this.fns[index]) {
19370
+ this.fns[index] = fn;
19371
+ return id;
19372
+ }
19373
+ return false;
19374
+ }
19375
+ use(fn) {
19376
+ this.fns.push(fn);
19377
+ return this.fns.length - 1;
19378
+ }
19379
+ }
19380
+ var createQuerySerializer7 = ({
19381
+ parameters = {},
19382
+ ...args
19383
+ } = {}) => {
19384
+ const querySerializer = (queryParams) => {
19385
+ const search = [];
19386
+ if (queryParams && typeof queryParams === "object") {
19387
+ for (const name in queryParams) {
19388
+ const value = queryParams[name];
19389
+ if (value === undefined || value === null) {
19390
+ continue;
19391
+ }
19392
+ const options = parameters[name] || args;
19393
+ if (Array.isArray(value)) {
19394
+ const serializedArray = serializeArrayParam7({
19395
+ allowReserved: options.allowReserved,
19396
+ explode: true,
19397
+ name,
19398
+ style: "form",
19399
+ value,
19400
+ ...options.array
19401
+ });
19402
+ if (serializedArray)
19403
+ search.push(serializedArray);
19404
+ } else if (typeof value === "object") {
19405
+ const serializedObject = serializeObjectParam7({
19406
+ allowReserved: options.allowReserved,
19407
+ explode: true,
19408
+ name,
19409
+ style: "deepObject",
19410
+ value,
19411
+ ...options.object
19412
+ });
19413
+ if (serializedObject)
19414
+ search.push(serializedObject);
19415
+ } else {
19416
+ const serializedPrimitive = serializePrimitiveParam7({
19417
+ allowReserved: options.allowReserved,
19418
+ name,
19419
+ value
19420
+ });
19421
+ if (serializedPrimitive)
19422
+ search.push(serializedPrimitive);
19423
+ }
19424
+ }
19425
+ }
19426
+ return search.join("&");
19427
+ };
19428
+ return querySerializer;
19429
+ }, getParseAs7 = (contentType) => {
19430
+ if (!contentType) {
19431
+ return "stream";
19432
+ }
19433
+ const cleanContent = contentType.split(";")[0]?.trim();
19434
+ if (!cleanContent) {
19435
+ return;
19436
+ }
19437
+ if (cleanContent.startsWith("application/json") || cleanContent.endsWith("+json")) {
19438
+ return "json";
19439
+ }
19440
+ if (cleanContent === "multipart/form-data") {
19441
+ return "formData";
19442
+ }
19443
+ if (["application/", "audio/", "image/", "video/"].some((type) => cleanContent.startsWith(type))) {
19444
+ return "blob";
19445
+ }
19446
+ if (cleanContent.startsWith("text/")) {
19447
+ return "text";
19448
+ }
19449
+ return;
19450
+ }, checkForExistence7 = (options, name) => {
19451
+ if (!name) {
19452
+ return false;
19453
+ }
19454
+ if (options.headers.has(name) || options.query?.[name] || options.headers.get("Cookie")?.includes(`${name}=`)) {
19455
+ return true;
19456
+ }
19457
+ return false;
19458
+ }, setAuthParams7 = async ({
19459
+ security,
19460
+ ...options
19461
+ }) => {
19462
+ for (const auth of security) {
19463
+ if (checkForExistence7(options, auth.name)) {
19464
+ continue;
19465
+ }
19466
+ const token = await getAuthToken7(auth, options.auth);
19467
+ if (!token) {
19468
+ continue;
19469
+ }
19470
+ const name = auth.name ?? "Authorization";
19471
+ switch (auth.in) {
19472
+ case "query":
19473
+ if (!options.query) {
19474
+ options.query = {};
19475
+ }
19476
+ options.query[name] = token;
19477
+ break;
19478
+ case "cookie":
19479
+ options.headers.append("Cookie", `${name}=${token}`);
19480
+ break;
19481
+ case "header":
19482
+ default:
19483
+ options.headers.set(name, token);
19484
+ break;
19485
+ }
19486
+ }
19487
+ }, buildUrl7 = (options) => getUrl7({
19488
+ baseUrl: options.baseUrl,
19489
+ path: options.path,
19490
+ query: options.query,
19491
+ querySerializer: typeof options.querySerializer === "function" ? options.querySerializer : createQuerySerializer7(options.querySerializer),
19492
+ url: options.url
19493
+ }), mergeConfigs7 = (a2, b2) => {
19494
+ const config = { ...a2, ...b2 };
19495
+ if (config.baseUrl?.endsWith("/")) {
19496
+ config.baseUrl = config.baseUrl.substring(0, config.baseUrl.length - 1);
19497
+ }
19498
+ config.headers = mergeHeaders7(a2.headers, b2.headers);
19499
+ return config;
19500
+ }, headersEntries7 = (headers) => {
19501
+ const entries = [];
19502
+ headers.forEach((value, key) => {
19503
+ entries.push([key, value]);
19504
+ });
19505
+ return entries;
19506
+ }, mergeHeaders7 = (...headers) => {
19507
+ const mergedHeaders = new Headers;
19508
+ for (const header of headers) {
19509
+ if (!header) {
19510
+ continue;
19511
+ }
19512
+ const iterator = header instanceof Headers ? headersEntries7(header) : Object.entries(header);
19513
+ for (const [key, value] of iterator) {
19514
+ if (value === null) {
19515
+ mergedHeaders.delete(key);
19516
+ } else if (Array.isArray(value)) {
19517
+ for (const v2 of value) {
19518
+ mergedHeaders.append(key, v2);
19519
+ }
19520
+ } else if (value !== undefined) {
19521
+ mergedHeaders.set(key, typeof value === "object" ? JSON.stringify(value) : value);
19522
+ }
19523
+ }
19524
+ }
19525
+ return mergedHeaders;
19526
+ }, createInterceptors7 = () => ({
19527
+ error: new Interceptors7,
19528
+ request: new Interceptors7,
19529
+ response: new Interceptors7
19530
+ }), defaultQuerySerializer7, defaultHeaders7, createConfig7 = (override = {}) => ({
19531
+ ...jsonBodySerializer7,
19532
+ headers: defaultHeaders7,
19533
+ parseAs: "auto",
19534
+ querySerializer: defaultQuerySerializer7,
19535
+ ...override
19536
+ });
19537
+ var init_utils_gen14 = __esm(() => {
19538
+ init_bodySerializer_gen7();
19539
+ init_utils_gen13();
19540
+ defaultQuerySerializer7 = createQuerySerializer7({
19541
+ allowReserved: false,
19542
+ array: {
19543
+ explode: true,
19544
+ style: "form"
19545
+ },
19546
+ object: {
19547
+ explode: true,
19548
+ style: "deepObject"
19549
+ }
19550
+ });
19551
+ defaultHeaders7 = {
19552
+ "Content-Type": "application/json"
19553
+ };
19554
+ });
19555
+
19556
+ // src/generated/qbittorrent/client/client.gen.ts
19557
+ var createClient7 = (config = {}) => {
19558
+ let _config = mergeConfigs7(createConfig7(), config);
19559
+ const getConfig = () => ({ ..._config });
19560
+ const setConfig = (config2) => {
19561
+ _config = mergeConfigs7(_config, config2);
19562
+ return getConfig();
19563
+ };
19564
+ const interceptors = createInterceptors7();
19565
+ const beforeRequest = async (options) => {
19566
+ const opts = {
19567
+ ..._config,
19568
+ ...options,
19569
+ fetch: options.fetch ?? _config.fetch ?? globalThis.fetch,
19570
+ headers: mergeHeaders7(_config.headers, options.headers),
19571
+ serializedBody: undefined
19572
+ };
19573
+ if (opts.security) {
19574
+ await setAuthParams7({
19575
+ ...opts,
19576
+ security: opts.security
19577
+ });
19578
+ }
19579
+ if (opts.requestValidator) {
19580
+ await opts.requestValidator(opts);
19581
+ }
19582
+ if (opts.body !== undefined && opts.bodySerializer) {
19583
+ opts.serializedBody = opts.bodySerializer(opts.body);
19584
+ }
19585
+ if (opts.body === undefined || opts.serializedBody === "") {
19586
+ opts.headers.delete("Content-Type");
19587
+ }
19588
+ const resolvedOpts = opts;
19589
+ const url = buildUrl7(resolvedOpts);
19590
+ return { opts: resolvedOpts, url };
19591
+ };
19592
+ const request = async (options) => {
19593
+ const { opts, url } = await beforeRequest(options);
19594
+ const requestInit = {
19595
+ redirect: "follow",
19596
+ ...opts,
19597
+ body: getValidRequestBody7(opts)
19598
+ };
19599
+ let request2 = new Request(url, requestInit);
19600
+ for (const fn of interceptors.request.fns) {
19601
+ if (fn) {
19602
+ request2 = await fn(request2, opts);
19603
+ }
19604
+ }
19605
+ const _fetch = opts.fetch;
19606
+ let response;
19607
+ try {
19608
+ response = await _fetch(request2);
19609
+ } catch (error2) {
19610
+ let finalError2 = error2;
19611
+ for (const fn of interceptors.error.fns) {
19612
+ if (fn) {
19613
+ finalError2 = await fn(error2, undefined, request2, opts);
19614
+ }
19615
+ }
19616
+ finalError2 = finalError2 || {};
19617
+ if (opts.throwOnError) {
19618
+ throw finalError2;
19619
+ }
19620
+ return opts.responseStyle === "data" ? undefined : {
19621
+ error: finalError2,
19622
+ request: request2,
19623
+ response: undefined
19624
+ };
19625
+ }
19626
+ for (const fn of interceptors.response.fns) {
19627
+ if (fn) {
19628
+ response = await fn(response, request2, opts);
19629
+ }
19630
+ }
19631
+ const result = {
19632
+ request: request2,
19633
+ response
19634
+ };
19635
+ if (response.ok) {
19636
+ const parseAs = (opts.parseAs === "auto" ? getParseAs7(response.headers.get("Content-Type")) : opts.parseAs) ?? "json";
19637
+ if (response.status === 204 || response.headers.get("Content-Length") === "0") {
19638
+ let emptyData;
19639
+ switch (parseAs) {
19640
+ case "arrayBuffer":
19641
+ case "blob":
19642
+ case "text":
19643
+ emptyData = await response[parseAs]();
19644
+ break;
19645
+ case "formData":
19646
+ emptyData = new FormData;
19647
+ break;
19648
+ case "stream":
19649
+ emptyData = response.body;
19650
+ break;
19651
+ case "json":
19652
+ default:
19653
+ emptyData = {};
19654
+ break;
19655
+ }
19656
+ return opts.responseStyle === "data" ? emptyData : {
19657
+ data: emptyData,
19658
+ ...result
19659
+ };
19660
+ }
19661
+ let data;
19662
+ switch (parseAs) {
19663
+ case "arrayBuffer":
19664
+ case "blob":
19665
+ case "formData":
19666
+ case "text":
19667
+ data = await response[parseAs]();
19668
+ break;
19669
+ case "json": {
19670
+ const text = await response.text();
19671
+ data = text ? JSON.parse(text) : {};
19672
+ break;
19673
+ }
19674
+ case "stream":
19675
+ return opts.responseStyle === "data" ? response.body : {
19676
+ data: response.body,
19677
+ ...result
19678
+ };
19679
+ }
19680
+ if (parseAs === "json") {
19681
+ if (opts.responseValidator) {
19682
+ await opts.responseValidator(data);
19683
+ }
19684
+ if (opts.responseTransformer) {
19685
+ data = await opts.responseTransformer(data);
19686
+ }
19687
+ }
19688
+ return opts.responseStyle === "data" ? data : {
19689
+ data,
19690
+ ...result
19691
+ };
19692
+ }
19693
+ const textError = await response.text();
19694
+ let jsonError;
19695
+ try {
19696
+ jsonError = JSON.parse(textError);
19697
+ } catch {}
19698
+ const error = jsonError ?? textError;
19699
+ let finalError = error;
19700
+ for (const fn of interceptors.error.fns) {
19701
+ if (fn) {
19702
+ finalError = await fn(error, response, request2, opts);
19703
+ }
19704
+ }
19705
+ finalError = finalError || {};
19706
+ if (opts.throwOnError) {
19707
+ throw finalError;
19708
+ }
19709
+ return opts.responseStyle === "data" ? undefined : {
19710
+ error: finalError,
19711
+ ...result
19712
+ };
19713
+ };
19714
+ const makeMethodFn = (method) => (options) => request({ ...options, method });
19715
+ const makeSseFn = (method) => async (options) => {
19716
+ const { opts, url } = await beforeRequest(options);
19717
+ return createSseClient7({
19718
+ ...opts,
19719
+ body: opts.body,
19720
+ headers: opts.headers,
19721
+ method,
19722
+ onRequest: async (url2, init2) => {
19723
+ let request2 = new Request(url2, init2);
19724
+ for (const fn of interceptors.request.fns) {
19725
+ if (fn) {
19726
+ request2 = await fn(request2, opts);
19727
+ }
19728
+ }
19729
+ return request2;
19730
+ },
19731
+ serializedBody: getValidRequestBody7(opts),
19732
+ url
19733
+ });
19734
+ };
19735
+ const _buildUrl = (options) => buildUrl7({ ..._config, ...options });
19736
+ return {
19737
+ buildUrl: _buildUrl,
19738
+ connect: makeMethodFn("CONNECT"),
19739
+ delete: makeMethodFn("DELETE"),
19740
+ get: makeMethodFn("GET"),
19741
+ getConfig,
19742
+ head: makeMethodFn("HEAD"),
19743
+ interceptors,
19744
+ options: makeMethodFn("OPTIONS"),
19745
+ patch: makeMethodFn("PATCH"),
19746
+ post: makeMethodFn("POST"),
19747
+ put: makeMethodFn("PUT"),
19748
+ request,
19749
+ setConfig,
19750
+ sse: {
19751
+ connect: makeSseFn("CONNECT"),
19752
+ delete: makeSseFn("DELETE"),
19753
+ get: makeSseFn("GET"),
19754
+ head: makeSseFn("HEAD"),
19755
+ options: makeSseFn("OPTIONS"),
19756
+ patch: makeSseFn("PATCH"),
19757
+ post: makeSseFn("POST"),
19758
+ put: makeSseFn("PUT"),
19759
+ trace: makeSseFn("TRACE")
19760
+ },
19761
+ trace: makeMethodFn("TRACE")
19762
+ };
19763
+ };
19764
+ var init_client_gen13 = __esm(() => {
19765
+ init_utils_gen13();
19766
+ init_utils_gen14();
19767
+ });
19768
+
19769
+ // src/generated/qbittorrent/client/index.ts
19770
+ var init_client8 = __esm(() => {
19771
+ init_bodySerializer_gen7();
19772
+ init_client_gen13();
19773
+ init_utils_gen14();
19774
+ });
19775
+
19776
+ // src/generated/qbittorrent/client.gen.ts
19777
+ var client7;
19778
+ var init_client_gen14 = __esm(() => {
19779
+ init_client8();
19780
+ client7 = createClient7(createConfig7({ baseUrl: "http://localhost:8080/api/v2" }));
19781
+ });
19782
+
19783
+ // src/generated/qbittorrent/sdk.gen.ts
19784
+ var appVersionGet = (options) => (options?.client ?? client7).get({
19785
+ security: [{
19786
+ in: "cookie",
19787
+ name: "SID",
19788
+ type: "apiKey"
19789
+ }],
19790
+ url: "/app/version",
19791
+ ...options
19792
+ }), appWebapiVersionGet = (options) => (options?.client ?? client7).get({
19793
+ security: [{
19794
+ in: "cookie",
19795
+ name: "SID",
19796
+ type: "apiKey"
19797
+ }],
19798
+ url: "/app/webapiVersion",
19799
+ ...options
19800
+ }), transferInfoGet = (options) => (options?.client ?? client7).get({
19801
+ security: [{
19802
+ in: "cookie",
19803
+ name: "SID",
19804
+ type: "apiKey"
19805
+ }],
19806
+ url: "/transfer/info",
19807
+ ...options
19808
+ }), torrentsInfoPost = (options) => (options.client ?? client7).post({
19809
+ ...urlSearchParamsBodySerializer2,
19810
+ security: [{
19811
+ in: "cookie",
19812
+ name: "SID",
19813
+ type: "apiKey"
19814
+ }],
19815
+ url: "/torrents/info",
19816
+ ...options,
19817
+ headers: {
19818
+ "Content-Type": "application/x-www-form-urlencoded",
19819
+ ...options.headers
19820
+ }
19821
+ }), torrentsPausePost = (options) => (options.client ?? client7).post({
19822
+ ...urlSearchParamsBodySerializer2,
19823
+ security: [{
19824
+ in: "cookie",
19825
+ name: "SID",
19826
+ type: "apiKey"
19827
+ }],
19828
+ url: "/torrents/pause",
19829
+ ...options,
19830
+ headers: {
19831
+ "Content-Type": "application/x-www-form-urlencoded",
19832
+ ...options.headers
19833
+ }
19834
+ }), torrentsResumePost = (options) => (options.client ?? client7).post({
19835
+ ...urlSearchParamsBodySerializer2,
19836
+ security: [{
19837
+ in: "cookie",
19838
+ name: "SID",
19839
+ type: "apiKey"
19840
+ }],
19841
+ url: "/torrents/resume",
19842
+ ...options,
19843
+ headers: {
19844
+ "Content-Type": "application/x-www-form-urlencoded",
19845
+ ...options.headers
19846
+ }
19847
+ }), torrentsDeletePost = (options) => (options.client ?? client7).post({
19848
+ ...urlSearchParamsBodySerializer2,
19849
+ security: [{
19850
+ in: "cookie",
19851
+ name: "SID",
19852
+ type: "apiKey"
19853
+ }],
19854
+ url: "/torrents/delete",
19855
+ ...options,
19856
+ headers: {
19857
+ "Content-Type": "application/x-www-form-urlencoded",
19858
+ ...options.headers
19859
+ }
19860
+ });
19861
+ var init_sdk_gen7 = __esm(() => {
19862
+ init_client8();
19863
+ init_client_gen14();
19864
+ });
19865
+
19866
+ // src/generated/qbittorrent/index.ts
19867
+ var init_qbittorrent = __esm(() => {
19868
+ init_sdk_gen7();
19869
+ });
19870
+
19871
+ // src/clients/qbittorrent.ts
19872
+ var exports_qbittorrent = {};
19873
+ __export(exports_qbittorrent, {
19874
+ QBittorrentClient: () => QBittorrentClient
19875
+ });
19876
+
19877
+ class QBittorrentClient {
19878
+ baseUrl;
19879
+ username;
19880
+ password;
19881
+ sid = null;
19882
+ constructor(config) {
19883
+ if (!config.baseUrl) {
19884
+ throw new ConnectionError("No base URL provided");
19885
+ }
19886
+ this.baseUrl = config.baseUrl.replace(/\/$/, "");
19887
+ this.username = config.username;
19888
+ this.password = config.password;
19889
+ client7.setConfig({
19890
+ baseUrl: `${this.baseUrl}/api/v2`,
19891
+ auth: () => this.ensureAuth()
19892
+ });
19893
+ }
19894
+ async ensureAuth() {
19895
+ if (!this.sid) {
19896
+ await this.login();
19897
+ }
19898
+ return this.sid;
19899
+ }
19900
+ async login() {
19901
+ const response = await fetch(`${this.baseUrl}/api/v2/auth/login`, {
19902
+ method: "POST",
19903
+ headers: {
19904
+ "Content-Type": "application/x-www-form-urlencoded",
19905
+ Referer: this.baseUrl
19906
+ },
19907
+ body: new URLSearchParams({
19908
+ username: this.username,
19909
+ password: this.password
19910
+ })
19911
+ });
19912
+ if (!response.ok) {
19913
+ throw new ConnectionError(`qBittorrent login failed (${response.status})`);
19914
+ }
19915
+ const text = await response.text();
19916
+ if (text.trim() !== "Ok.") {
19917
+ throw new ConnectionError("qBittorrent authentication failed: invalid username or password");
19918
+ }
19919
+ const setCookie = response.headers.get("set-cookie");
19920
+ const sidMatch = setCookie?.match(/SID=([^;]+)/);
19921
+ if (!sidMatch) {
19922
+ throw new ConnectionError("qBittorrent login succeeded but no SID cookie received");
19923
+ }
19924
+ this.sid = sidMatch[1];
19925
+ }
19926
+ async getAppVersion() {
19927
+ const result = await appVersionGet();
19928
+ return result.data ?? "";
19929
+ }
19930
+ async getApiVersion() {
19931
+ const result = await appWebapiVersionGet();
19932
+ return result.data ?? "";
19933
+ }
19934
+ async getSystemStatus() {
19935
+ const version = await this.getAppVersion();
19936
+ return { version };
19937
+ }
19938
+ async getTransferInfo() {
19939
+ const result = await transferInfoGet();
19940
+ return result.data ?? {};
19941
+ }
19942
+ async getTorrents(filter) {
19943
+ const result = await torrentsInfoPost({
19944
+ body: {
19945
+ ...filter ? { filter } : {}
19946
+ }
19947
+ });
19948
+ return result.data ?? [];
19949
+ }
19950
+ async pauseTorrents(hashes) {
19951
+ await torrentsPausePost({
19952
+ body: { hashes: hashes.split("|") }
19953
+ });
19954
+ }
19955
+ async resumeTorrents(hashes) {
19956
+ await torrentsResumePost({
19957
+ body: { hashes: hashes.split("|") }
19958
+ });
19959
+ }
19960
+ async deleteTorrents(hashes, deleteFiles = false) {
19961
+ await torrentsDeletePost({
19962
+ body: { hashes: hashes.split("|"), deleteFiles }
19963
+ });
19964
+ }
19965
+ }
19966
+ var init_qbittorrent2 = __esm(() => {
19967
+ init_errors();
19968
+ init_client_gen14();
19969
+ init_qbittorrent();
19970
+ });
19971
+
19972
+ // src/cli/commands/qbit.ts
19973
+ var exports_qbit = {};
19974
+ __export(exports_qbit, {
19975
+ resources: () => resources7,
19976
+ qbit: () => qbit
19977
+ });
19978
+ var resources7, qbit;
19979
+ var init_qbit = __esm(() => {
19980
+ init_qbittorrent2();
19981
+ init_service();
19982
+ resources7 = [
19983
+ {
19984
+ name: "torrents",
19985
+ description: "Manage torrents",
19986
+ actions: [
19987
+ {
19988
+ name: "list",
19989
+ description: "List torrents",
19990
+ args: [
19991
+ {
19992
+ name: "filter",
19993
+ description: "Filter (all|downloading|seeding|completed|paused|active|inactive|stalled|errored)"
19994
+ }
19995
+ ],
19996
+ columns: ["hash", "name", "state", "progress", "size", "dlspeed", "upspeed"],
19997
+ idField: "hash",
19998
+ run: (c3, a2) => c3.getTorrents(a2.filter)
19999
+ },
20000
+ {
20001
+ name: "pause",
20002
+ description: "Pause torrents",
20003
+ args: [
20004
+ {
20005
+ name: "hashes",
20006
+ description: 'Torrent hashes (comma-separated, or "all")',
20007
+ required: true
20008
+ }
20009
+ ],
20010
+ run: (c3, a2) => c3.pauseTorrents(a2.hashes)
20011
+ },
20012
+ {
20013
+ name: "resume",
20014
+ description: "Resume torrents",
20015
+ args: [
20016
+ {
20017
+ name: "hashes",
20018
+ description: 'Torrent hashes (comma-separated, or "all")',
20019
+ required: true
20020
+ }
20021
+ ],
20022
+ run: (c3, a2) => c3.resumeTorrents(a2.hashes)
20023
+ },
20024
+ {
20025
+ name: "delete",
20026
+ description: "Delete torrents",
20027
+ args: [
20028
+ {
20029
+ name: "hashes",
20030
+ description: 'Torrent hashes (comma-separated, or "all")',
20031
+ required: true
20032
+ },
20033
+ {
20034
+ name: "delete-files",
20035
+ description: "Also delete downloaded files",
20036
+ type: "boolean"
20037
+ }
20038
+ ],
20039
+ confirmMessage: "Are you sure you want to delete these torrents?",
20040
+ run: (c3, a2) => c3.deleteTorrents(a2.hashes, a2["delete-files"])
20041
+ }
20042
+ ]
20043
+ },
20044
+ {
20045
+ name: "status",
20046
+ description: "Transfer status",
20047
+ actions: [
20048
+ {
20049
+ name: "show",
20050
+ description: "Show transfer info (speed, connections)",
20051
+ columns: [
20052
+ "connection_status",
20053
+ "dl_info_speed",
20054
+ "dl_info_data",
20055
+ "up_info_speed",
20056
+ "up_info_data",
20057
+ "dht_nodes"
20058
+ ],
20059
+ run: (c3) => c3.getTransferInfo()
20060
+ }
20061
+ ]
20062
+ }
20063
+ ];
20064
+ qbit = buildServiceCommand("qbittorrent", "Manage qBittorrent", (config) => new QBittorrentClient(config), resources7);
20065
+ });
20066
+
20067
+ // src/cli/commands/doctor.ts
20068
+ var exports_doctor = {};
20069
+ __export(exports_doctor, {
20070
+ doctor: () => doctor
20071
+ });
20072
+ function classifyError(error) {
20073
+ if (!(error instanceof Error))
20074
+ return "Unknown error";
20075
+ const msg = error.message;
20076
+ const cause = error.cause;
20077
+ if (cause?.code === "ECONNREFUSED" || msg.includes("ECONNREFUSED")) {
20078
+ return "Connection refused - is the service running?";
20079
+ }
20080
+ if (cause?.code === "ENOTFOUND" || msg.includes("ENOTFOUND")) {
20081
+ return "Host not found - check the URL";
20082
+ }
20083
+ if (cause?.code === "ECONNRESET" || msg.includes("ECONNRESET")) {
20084
+ return "Connection reset - service may have crashed";
20085
+ }
20086
+ if (cause?.code === "ETIMEDOUT" || msg.includes("ETIMEDOUT") || msg.includes("timed out")) {
20087
+ return "Connection timed out - service may be unreachable";
20088
+ }
20089
+ if (msg.includes("fetch failed") || msg.includes("Failed to fetch")) {
20090
+ return `Service unreachable - ${cause?.message ?? "check URL and network"}`;
20091
+ }
20092
+ if (msg.includes("401") || msg.includes("Unauthorized")) {
20093
+ return "Authentication failed (401) - check your API key";
20094
+ }
20095
+ if (msg.includes("403") || msg.includes("Forbidden")) {
20096
+ return "Access denied (403) - check your API key permissions";
20097
+ }
20098
+ if (msg.includes("502") || msg.includes("Bad Gateway")) {
20099
+ return "Bad gateway (502) - reverse proxy or service issue";
20100
+ }
20101
+ if (msg.includes("503") || msg.includes("Service Unavailable")) {
20102
+ return "Service unavailable (503) - service may be starting up";
20103
+ }
20104
+ if (msg.includes("CERT") || msg.includes("certificate") || msg.includes("SSL")) {
20105
+ return "SSL/TLS certificate error - check HTTPS configuration";
20106
+ }
20107
+ return msg;
20108
+ }
20109
+ function extractVersion(service, status) {
20110
+ const data = status?.data ?? status;
20111
+ if (typeof data === "string") {
20112
+ return null;
20113
+ }
20114
+ if (service === "bazarr") {
20115
+ return data?.data?.bazarr_version ?? data?.bazarr_version ?? null;
20116
+ }
20117
+ if (service === "qbittorrent") {
20118
+ return data?.version ?? null;
20119
+ }
20120
+ return data?.version ?? status?.version ?? null;
20121
+ }
20122
+ var clientFactories, doctor;
20123
+ var init_doctor = __esm(() => {
20124
+ init_dist();
20125
+ init_dist2();
20126
+ init_bazarr2();
20127
+ init_lidarr2();
20128
+ init_prowlarr2();
20129
+ init_qbittorrent2();
20130
+ init_radarr2();
20131
+ init_readarr2();
20132
+ init_sonarr2();
20133
+ init_config();
20134
+ init_output();
20135
+ clientFactories = {
20136
+ radarr: (c3) => new RadarrClient(c3),
20137
+ sonarr: (c3) => new SonarrClient(c3),
20138
+ lidarr: (c3) => new LidarrClient(c3),
20139
+ readarr: (c3) => new ReadarrClient(c3),
20140
+ prowlarr: (c3) => new ProwlarrClient(c3),
20141
+ bazarr: (c3) => new BazarrClient(c3),
20142
+ qbittorrent: (c3) => new QBittorrentClient(c3)
20143
+ };
20144
+ doctor = defineCommand({
20145
+ meta: {
20146
+ name: "doctor",
20147
+ description: "Test all configured service connections"
20148
+ },
20149
+ args: {
20150
+ json: { type: "boolean", description: "Output as JSON" },
20151
+ table: { type: "boolean", description: "Output as table" },
20152
+ plain: { type: "boolean", description: "Output as TSV (no colors, for piping)" },
20153
+ quiet: { type: "boolean", alias: "q", description: "Output service names only" },
20154
+ select: { type: "string", description: "Cherry-pick fields (comma-separated, JSON mode)" }
20155
+ },
18407
20156
  async run({ args }) {
18408
20157
  const format = detectFormat(args);
18409
20158
  let hasAny = false;
@@ -18435,8 +20184,8 @@ var init_doctor = __esm(() => {
18435
20184
  });
18436
20185
  continue;
18437
20186
  }
18438
- const client7 = factory(svcConfig);
18439
- const status = await client7.getSystemStatus();
20187
+ const client8 = factory(svcConfig);
20188
+ const status = await client8.getSystemStatus();
18440
20189
  const version = extractVersion(service, status) ?? "?";
18441
20190
  if (version === "?") {
18442
20191
  throw new Error("Unexpected response payload");
@@ -18492,7 +20241,8 @@ var init_config2 = __esm(() => {
18492
20241
  lidarr: 8686,
18493
20242
  readarr: 8787,
18494
20243
  prowlarr: 9696,
18495
- bazarr: 6767
20244
+ bazarr: 6767,
20245
+ qbittorrent: 8080
18496
20246
  };
18497
20247
  configInit = defineCommand({
18498
20248
  meta: {
@@ -18518,32 +20268,45 @@ var init_config2 = __esm(() => {
18518
20268
  for (const service of selected) {
18519
20269
  console.log();
18520
20270
  const baseUrl = await promptIfMissing(undefined, `${service} base URL (e.g. http://localhost:${DEFAULT_PORTS[service]})`);
18521
- const apiKey = await promptIfMissing(undefined, `${service} API key`);
18522
- config.services[service] = { baseUrl, apiKey };
18523
- const svcConfig = { baseUrl, apiKey };
18524
- try {
18525
- const { RadarrClient: RadarrClient2 } = await Promise.resolve().then(() => (init_radarr2(), exports_radarr));
18526
- const { SonarrClient: SonarrClient2 } = await Promise.resolve().then(() => (init_sonarr2(), exports_sonarr));
18527
- const { LidarrClient: LidarrClient2 } = await Promise.resolve().then(() => (init_lidarr2(), exports_lidarr));
18528
- const { ReadarrClient: ReadarrClient2 } = await Promise.resolve().then(() => (init_readarr2(), exports_readarr));
18529
- const { ProwlarrClient: ProwlarrClient2 } = await Promise.resolve().then(() => (init_prowlarr2(), exports_prowlarr));
18530
- const { BazarrClient: BazarrClient2 } = await Promise.resolve().then(() => (init_bazarr2(), exports_bazarr));
18531
- const factories = {
18532
- radarr: (c3) => new RadarrClient2(c3),
18533
- sonarr: (c3) => new SonarrClient2(c3),
18534
- lidarr: (c3) => new LidarrClient2(c3),
18535
- readarr: (c3) => new ReadarrClient2(c3),
18536
- prowlarr: (c3) => new ProwlarrClient2(c3),
18537
- bazarr: (c3) => new BazarrClient2(c3)
18538
- };
18539
- const client7 = factories[service]?.(svcConfig);
18540
- if (client7) {
18541
- const status = await client7.getSystemStatus();
18542
- const version = status?.data?.version ?? status?.version ?? "?";
18543
- consola.success(`Connected to ${service} v${version}`);
20271
+ if (service === "qbittorrent") {
20272
+ const username = await promptIfMissing(undefined, `${service} username`);
20273
+ const password = await promptIfMissing(undefined, `${service} password`);
20274
+ config.services[service] = { baseUrl, username, password };
20275
+ try {
20276
+ const { QBittorrentClient: QBittorrentClient2 } = await Promise.resolve().then(() => (init_qbittorrent2(), exports_qbittorrent));
20277
+ const client8 = new QBittorrentClient2({ baseUrl, username, password });
20278
+ const status = await client8.getSystemStatus();
20279
+ consola.success(`Connected to ${service} v${status.version}`);
20280
+ } catch {
20281
+ consola.warn(`Could not connect to ${service} — config saved anyway.`);
20282
+ }
20283
+ } else {
20284
+ const apiKey = await promptIfMissing(undefined, `${service} API key`);
20285
+ config.services[service] = { baseUrl, apiKey };
20286
+ try {
20287
+ const { RadarrClient: RadarrClient2 } = await Promise.resolve().then(() => (init_radarr2(), exports_radarr));
20288
+ const { SonarrClient: SonarrClient2 } = await Promise.resolve().then(() => (init_sonarr2(), exports_sonarr));
20289
+ const { LidarrClient: LidarrClient2 } = await Promise.resolve().then(() => (init_lidarr2(), exports_lidarr));
20290
+ const { ReadarrClient: ReadarrClient2 } = await Promise.resolve().then(() => (init_readarr2(), exports_readarr));
20291
+ const { ProwlarrClient: ProwlarrClient2 } = await Promise.resolve().then(() => (init_prowlarr2(), exports_prowlarr));
20292
+ const { BazarrClient: BazarrClient2 } = await Promise.resolve().then(() => (init_bazarr2(), exports_bazarr));
20293
+ const factories = {
20294
+ radarr: (c3) => new RadarrClient2(c3),
20295
+ sonarr: (c3) => new SonarrClient2(c3),
20296
+ lidarr: (c3) => new LidarrClient2(c3),
20297
+ readarr: (c3) => new ReadarrClient2(c3),
20298
+ prowlarr: (c3) => new ProwlarrClient2(c3),
20299
+ bazarr: (c3) => new BazarrClient2(c3)
20300
+ };
20301
+ const client8 = factories[service]?.(config.services[service]);
20302
+ if (client8) {
20303
+ const status = await client8.getSystemStatus();
20304
+ const version = status?.data?.version ?? status?.version ?? "?";
20305
+ consola.success(`Connected to ${service} v${version}`);
20306
+ }
20307
+ } catch {
20308
+ consola.warn(`Could not connect to ${service} — config saved anyway.`);
18544
20309
  }
18545
- } catch {
18546
- consola.warn(`Could not connect to ${service} — config saved anyway.`);
18547
20310
  }
18548
20311
  }
18549
20312
  const location = await promptSelect("Save config to:", [
@@ -18610,6 +20373,8 @@ var init_config2 = __esm(() => {
18610
20373
  for (const svc of Object.values(redacted.services)) {
18611
20374
  if (svc?.apiKey)
18612
20375
  svc.apiKey = "*****";
20376
+ if (svc?.password)
20377
+ svc.password = "*****";
18613
20378
  }
18614
20379
  }
18615
20380
  console.log(JSON.stringify(redacted, null, 2));
@@ -18637,11 +20402,11 @@ __export(exports_completions, {
18637
20402
  function generateBashCompletion() {
18638
20403
  const services = Object.keys(SERVICE_COMMANDS).join(" ");
18639
20404
  const globals = "doctor config completions";
18640
- const resourceVars = Object.entries(SERVICE_COMMANDS).map(([svc, resources7]) => ` local ${svc}_resources="${Object.keys(resources7).join(" ")}"`).join(`
20405
+ const resourceVars = Object.entries(SERVICE_COMMANDS).map(([svc, resources8]) => ` local ${svc}_resources="${Object.keys(resources8).join(" ")}"`).join(`
18641
20406
  `);
18642
20407
  const resourceCases = Object.keys(SERVICE_COMMANDS).map((svc) => ` ${svc}) COMPREPLY=( $(compgen -W "$${svc}_resources" -- "$cur") ) ;;`).join(`
18643
20408
  `);
18644
- const actionCases = Object.entries(SERVICE_COMMANDS).flatMap(([svc, resources7]) => Object.entries(resources7).map(([res, actions]) => ` ${res}) [[ "\${COMP_WORDS[1]}" == "${svc}" ]] && COMPREPLY=( $(compgen -W "${actions.join(" ")}" -- "$cur") ) ;;`)).join(`
20409
+ const actionCases = Object.entries(SERVICE_COMMANDS).flatMap(([svc, resources8]) => Object.entries(resources8).map(([res, actions]) => ` ${res}) [[ "\${COMP_WORDS[1]}" == "${svc}" ]] && COMPREPLY=( $(compgen -W "${actions.join(" ")}" -- "$cur") ) ;;`)).join(`
18645
20410
  `);
18646
20411
  return `#!/bin/bash
18647
20412
  _tsarr_completions() {
@@ -18675,9 +20440,9 @@ complete -F _tsarr_completions tsarr`;
18675
20440
  }
18676
20441
  function generateZshCompletion() {
18677
20442
  const services = Object.keys(SERVICE_COMMANDS).join(" ");
18678
- const resourceAssoc = Object.entries(SERVICE_COMMANDS).map(([svc, resources7]) => ` ${svc} "${Object.keys(resources7).join(" ")}"`).join(`
20443
+ const resourceAssoc = Object.entries(SERVICE_COMMANDS).map(([svc, resources8]) => ` ${svc} "${Object.keys(resources8).join(" ")}"`).join(`
18679
20444
  `);
18680
- const actionAssoc = Object.entries(SERVICE_COMMANDS).flatMap(([svc, resources7]) => Object.entries(resources7).map(([res, actions]) => ` ${svc}:${res} "${actions.join(" ")}"`)).join(`
20445
+ const actionAssoc = Object.entries(SERVICE_COMMANDS).flatMap(([svc, resources8]) => Object.entries(resources8).map(([res, actions]) => ` ${svc}:${res} "${actions.join(" ")}"`)).join(`
18681
20446
  `);
18682
20447
  return `#compdef tsarr
18683
20448
 
@@ -18734,12 +20499,12 @@ _tsarr "$@"`;
18734
20499
  function generateFishCompletion() {
18735
20500
  const services = Object.keys(SERVICE_COMMANDS).join(" ");
18736
20501
  const globals = "doctor config completions";
18737
- const resourceCompletions = Object.entries(SERVICE_COMMANDS).map(([svc, resources7]) => {
18738
- const res = Object.keys(resources7).join(" ");
20502
+ const resourceCompletions = Object.entries(SERVICE_COMMANDS).map(([svc, resources8]) => {
20503
+ const res = Object.keys(resources8).join(" ");
18739
20504
  return `complete -c tsarr -n "__fish_seen_subcommand_from ${svc}; and not __fish_seen_subcommand_from ${res}" -a "${res}"`;
18740
20505
  }).join(`
18741
20506
  `);
18742
- 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(`
20507
+ const actionCompletions = Object.entries(SERVICE_COMMANDS).flatMap(([svc, resources8]) => Object.entries(resources8).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(`
18743
20508
  `);
18744
20509
  return `# Fish completions for tsarr
18745
20510
  set -l services ${services}
@@ -18854,7 +20619,7 @@ init_dist();
18854
20619
  // package.json
18855
20620
  var package_default = {
18856
20621
  name: "tsarr",
18857
- version: "2.4.12",
20622
+ version: "2.6.0",
18858
20623
  author: "Robbe Verhelst",
18859
20624
  repository: {
18860
20625
  type: "git",
@@ -18866,16 +20631,16 @@ var package_default = {
18866
20631
  main: "dist/index.js",
18867
20632
  module: "dist/index.js",
18868
20633
  devDependencies: {
18869
- "@biomejs/biome": "2.4.8",
18870
- "@hey-api/openapi-ts": "^0.94.3",
20634
+ "@biomejs/biome": "2.4.11",
20635
+ "@hey-api/openapi-ts": "^0.96.0",
18871
20636
  "@semantic-release/changelog": "^6.0.3",
18872
20637
  "@semantic-release/git": "^10.0.1",
18873
20638
  "@semantic-release/github": "^12.0.6",
18874
20639
  "@semantic-release/npm": "^13.1.5",
18875
- "@types/bun": "^1.3.10",
20640
+ "@types/bun": "^1.3.12",
18876
20641
  "semantic-release": "^25.0.3",
18877
- typedoc: "^0.28.17",
18878
- typescript: "^5.9.3"
20642
+ typedoc: "^0.28.19",
20643
+ typescript: "^6.0.2"
18879
20644
  },
18880
20645
  exports: {
18881
20646
  ".": {
@@ -18906,6 +20671,10 @@ var package_default = {
18906
20671
  import: "./dist/clients/bazarr.js",
18907
20672
  types: "./dist/clients/bazarr.d.ts"
18908
20673
  },
20674
+ "./qbittorrent": {
20675
+ import: "./dist/clients/qbittorrent.js",
20676
+ types: "./dist/clients/qbittorrent.d.ts"
20677
+ },
18909
20678
  "./radarr/types": {
18910
20679
  types: "./dist/clients/radarr-types.d.ts"
18911
20680
  },
@@ -18923,11 +20692,14 @@ var package_default = {
18923
20692
  },
18924
20693
  "./bazarr/types": {
18925
20694
  types: "./dist/clients/bazarr-types.d.ts"
20695
+ },
20696
+ "./qbittorrent/types": {
20697
+ types: "./dist/clients/qbittorrent-types.d.ts"
18926
20698
  }
18927
20699
  },
18928
20700
  description: "Type-safe TypeScript SDK for Servarr APIs (Radarr, Sonarr, etc.)",
18929
20701
  engines: {
18930
- node: ">=18.20.8"
20702
+ node: ">=24.14.1"
18931
20703
  },
18932
20704
  files: [
18933
20705
  "dist",
@@ -18941,6 +20713,7 @@ var package_default = {
18941
20713
  "readarr",
18942
20714
  "prowlarr",
18943
20715
  "bazarr",
20716
+ "qbittorrent",
18944
20717
  "typescript",
18945
20718
  "sdk",
18946
20719
  "api",
@@ -18954,7 +20727,7 @@ var package_default = {
18954
20727
  },
18955
20728
  scripts: {
18956
20729
  build: "bun run generate && bun run generate:types && bun run build:js && bun run build:cli && bun run build:types",
18957
- "build:js": "bun build src/index.ts --outdir dist --target node --minify --splitting && bun build src/clients/radarr.ts --outdir dist/clients --target node --format esm && bun build src/clients/sonarr.ts --outdir dist/clients --target node --format esm && bun build src/clients/lidarr.ts --outdir dist/clients --target node --format esm && bun build src/clients/readarr.ts --outdir dist/clients --target node --format esm && bun build src/clients/prowlarr.ts --outdir dist/clients --target node --format esm && bun build src/clients/bazarr.ts --outdir dist/clients --target node --format esm",
20730
+ "build:js": "bun build src/index.ts --outdir dist --target node --minify --splitting && bun build src/clients/radarr.ts --outdir dist/clients --target node --format esm && bun build src/clients/sonarr.ts --outdir dist/clients --target node --format esm && bun build src/clients/lidarr.ts --outdir dist/clients --target node --format esm && bun build src/clients/readarr.ts --outdir dist/clients --target node --format esm && bun build src/clients/prowlarr.ts --outdir dist/clients --target node --format esm && bun build src/clients/bazarr.ts --outdir dist/clients --target node --format esm && bun build src/clients/qbittorrent.ts --outdir dist/clients --target node --format esm",
18958
20731
  "build:cli": "bun build src/cli/index.ts --outdir dist/cli --target node --format esm && chmod +x dist/cli/index.js",
18959
20732
  "build:types": "tsc --project tsconfig.build.json",
18960
20733
  prepublishOnly: "bun run build",
@@ -18981,7 +20754,7 @@ var package_default = {
18981
20754
  type: "module",
18982
20755
  types: "dist/index.d.ts",
18983
20756
  dependencies: {
18984
- citty: "^0.2.1",
20757
+ citty: "^0.2.2",
18985
20758
  consola: "^3.4.2"
18986
20759
  }
18987
20760
  };
@@ -18992,7 +20765,7 @@ var main = defineCommand({
18992
20765
  meta: {
18993
20766
  name: "tsarr",
18994
20767
  version,
18995
- description: "Type-safe CLI for Servarr APIs (Radarr, Sonarr, Lidarr, Readarr, Prowlarr, Bazarr)"
20768
+ description: "Type-safe CLI for Servarr APIs (Radarr, Sonarr, Lidarr, Readarr, Prowlarr, Bazarr, qBittorrent)"
18996
20769
  },
18997
20770
  subCommands: {
18998
20771
  radarr: () => Promise.resolve().then(() => (init_radarr3(), exports_radarr3)).then((m2) => m2.radarr),
@@ -19001,6 +20774,7 @@ var main = defineCommand({
19001
20774
  readarr: () => Promise.resolve().then(() => (init_readarr3(), exports_readarr3)).then((m2) => m2.readarr),
19002
20775
  prowlarr: () => Promise.resolve().then(() => (init_prowlarr3(), exports_prowlarr3)).then((m2) => m2.prowlarr),
19003
20776
  bazarr: () => Promise.resolve().then(() => (init_bazarr3(), exports_bazarr3)).then((m2) => m2.bazarr),
20777
+ qbit: () => Promise.resolve().then(() => (init_qbit(), exports_qbit)).then((m2) => m2.qbit),
19004
20778
  doctor: () => Promise.resolve().then(() => (init_doctor(), exports_doctor)).then((m2) => m2.doctor),
19005
20779
  config: () => Promise.resolve().then(() => (init_config2(), exports_config)).then((m2) => m2.config),
19006
20780
  completions: () => Promise.resolve().then(() => (init_completions(), exports_completions)).then((m2) => m2.completions)