tsarr 2.4.1 → 2.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/index.js CHANGED
@@ -5059,6 +5059,14 @@ var init_output = __esm(() => {
5059
5059
  });
5060
5060
 
5061
5061
  // src/cli/commands/service.ts
5062
+ function limitResults(results, limit) {
5063
+ if (limit === undefined)
5064
+ return results;
5065
+ if (!Number.isInteger(limit) || limit < 1) {
5066
+ throw new Error("--limit must be a positive integer.");
5067
+ }
5068
+ return results.slice(0, limit);
5069
+ }
5062
5070
  function buildServiceCommand(serviceName, description, clientFactory, resources) {
5063
5071
  const subCommands = {};
5064
5072
  for (const resource of resources) {
@@ -5245,6 +5253,7 @@ Run \`tsarr doctor\` to diagnose.`);
5245
5253
  }
5246
5254
  process.exit(1);
5247
5255
  }
5256
+ var COMMAND_OUTPUT_COLUMNS;
5248
5257
  var init_service = __esm(() => {
5249
5258
  init_dist();
5250
5259
  init_dist2();
@@ -5252,11 +5261,13 @@ var init_service = __esm(() => {
5252
5261
  init_config();
5253
5262
  init_output();
5254
5263
  init_prompt2();
5264
+ COMMAND_OUTPUT_COLUMNS = ["id", "name", "status", "result", "queued", "trigger"];
5255
5265
  });
5256
5266
 
5257
5267
  // src/cli/commands/radarr.ts
5258
5268
  var exports_radarr3 = {};
5259
5269
  __export(exports_radarr3, {
5270
+ resources: () => resources,
5260
5271
  radarr: () => radarr
5261
5272
  });
5262
5273
  import { readFileSync as readFileSync2 } from "node:fs";
@@ -5330,10 +5341,16 @@ var init_radarr3 = __esm(() => {
5330
5341
  {
5331
5342
  name: "search",
5332
5343
  description: "Search for movies on TMDB",
5333
- args: [{ name: "term", description: "Search term", required: true }],
5344
+ args: [
5345
+ { name: "term", description: "Search term", required: true },
5346
+ { name: "limit", description: "Max results to show", type: "number" }
5347
+ ],
5334
5348
  columns: ["tmdbId", "title", "year", "overview"],
5335
5349
  idField: "tmdbId",
5336
- run: (c3, a2) => c3.searchMovies(a2.term)
5350
+ run: async (c3, a2) => {
5351
+ const results = unwrapData(await c3.searchMovies(a2.term));
5352
+ return limitResults(results, a2.limit);
5353
+ }
5337
5354
  },
5338
5355
  {
5339
5356
  name: "add",
@@ -5428,12 +5445,14 @@ var init_radarr3 = __esm(() => {
5428
5445
  name: "refresh",
5429
5446
  description: "Refresh movie metadata",
5430
5447
  args: [{ name: "id", description: "Movie ID", required: true, type: "number" }],
5448
+ columns: COMMAND_OUTPUT_COLUMNS,
5431
5449
  run: (c3, a2) => c3.runCommand({ name: "RefreshMovie", movieIds: [a2.id] })
5432
5450
  },
5433
5451
  {
5434
5452
  name: "manual-search",
5435
5453
  description: "Trigger a manual search for releases",
5436
5454
  args: [{ name: "id", description: "Movie ID", required: true, type: "number" }],
5455
+ columns: COMMAND_OUTPUT_COLUMNS,
5437
5456
  run: (c3, a2) => c3.runCommand({ name: "MoviesSearch", movieIds: [a2.id] })
5438
5457
  },
5439
5458
  {
@@ -8089,6 +8108,7 @@ class SonarrClient {
8089
8108
  query.end = endDate;
8090
8109
  if (unmonitored !== undefined)
8091
8110
  query.unmonitored = unmonitored;
8111
+ query.includeSeries = true;
8092
8112
  return getApiV3Calendar2(Object.keys(query).length > 0 ? { query } : {});
8093
8113
  }
8094
8114
  async getCalendarFeed(pastDays, futureDays, tags) {
@@ -8235,7 +8255,8 @@ var init_sonarr2 = __esm(() => {
8235
8255
  // src/cli/commands/sonarr.ts
8236
8256
  var exports_sonarr3 = {};
8237
8257
  __export(exports_sonarr3, {
8238
- sonarr: () => sonarr
8258
+ sonarr: () => sonarr,
8259
+ resources: () => resources2
8239
8260
  });
8240
8261
  import { readFileSync as readFileSync3 } from "node:fs";
8241
8262
  function unwrapData2(result) {
@@ -8352,9 +8373,15 @@ var init_sonarr3 = __esm(() => {
8352
8373
  {
8353
8374
  name: "search",
8354
8375
  description: "Search for TV series",
8355
- args: [{ name: "term", description: "Search term", required: true }],
8376
+ args: [
8377
+ { name: "term", description: "Search term", required: true },
8378
+ { name: "limit", description: "Max results to show", type: "number" }
8379
+ ],
8356
8380
  columns: ["tvdbId", "title", "year", "overview"],
8357
- run: (c3, a2) => c3.searchSeries(a2.term)
8381
+ run: async (c3, a2) => {
8382
+ const results = unwrapData2(await c3.searchSeries(a2.term));
8383
+ return limitResults(results, a2.limit);
8384
+ }
8358
8385
  },
8359
8386
  {
8360
8387
  name: "add",
@@ -8446,12 +8473,14 @@ var init_sonarr3 = __esm(() => {
8446
8473
  name: "refresh",
8447
8474
  description: "Refresh series metadata",
8448
8475
  args: [{ name: "id", description: "Series ID", required: true, type: "number" }],
8476
+ columns: COMMAND_OUTPUT_COLUMNS,
8449
8477
  run: (c3, a2) => c3.runCommand({ name: "RefreshSeries", seriesId: a2.id })
8450
8478
  },
8451
8479
  {
8452
8480
  name: "manual-search",
8453
8481
  description: "Trigger a manual search for releases",
8454
8482
  args: [{ name: "id", description: "Series ID", required: true, type: "number" }],
8483
+ columns: COMMAND_OUTPUT_COLUMNS,
8455
8484
  run: (c3, a2) => c3.runCommand({ name: "SeriesSearch", seriesId: a2.id })
8456
8485
  },
8457
8486
  {
@@ -8504,6 +8533,7 @@ var init_sonarr3 = __esm(() => {
8504
8533
  name: "search",
8505
8534
  description: "Trigger a search for an episode",
8506
8535
  args: [{ name: "id", description: "Episode ID", required: true, type: "number" }],
8536
+ columns: COMMAND_OUTPUT_COLUMNS,
8507
8537
  run: (c3, a2) => c3.runCommand({ name: "EpisodeSearch", episodeIds: [a2.id] })
8508
8538
  }
8509
8539
  ]
@@ -9968,6 +9998,14 @@ var getApiV1Album = (options) => (options?.client ?? client3).get({
9968
9998
  }],
9969
9999
  url: "/api/v1/customformat/schema",
9970
10000
  ...options
10001
+ }), getApiV1WantedCutoff = (options) => (options?.client ?? client3).get({
10002
+ security: [{ name: "X-Api-Key", type: "apiKey" }, {
10003
+ in: "query",
10004
+ name: "apikey",
10005
+ type: "apiKey"
10006
+ }],
10007
+ url: "/api/v1/wanted/cutoff",
10008
+ ...options
9971
10009
  }), getApiV1Diskspace = (options) => (options?.client ?? client3).get({
9972
10010
  security: [{ name: "X-Api-Key", type: "apiKey" }, {
9973
10011
  in: "query",
@@ -10376,6 +10414,14 @@ var getApiV1Album = (options) => (options?.client ?? client3).get({
10376
10414
  }],
10377
10415
  url: "/api/v1/config/metadataprovider",
10378
10416
  ...options
10417
+ }), getApiV1WantedMissing = (options) => (options?.client ?? client3).get({
10418
+ security: [{ name: "X-Api-Key", type: "apiKey" }, {
10419
+ in: "query",
10420
+ name: "apikey",
10421
+ type: "apiKey"
10422
+ }],
10423
+ url: "/api/v1/wanted/missing",
10424
+ ...options
10379
10425
  }), getApiV1ConfigNamingById = (options) => (options.client ?? client3).get({
10380
10426
  security: [{ name: "X-Api-Key", type: "apiKey" }, {
10381
10427
  in: "query",
@@ -10844,6 +10890,7 @@ class LidarrClient {
10844
10890
  query.end = end;
10845
10891
  if (unmonitored !== undefined)
10846
10892
  query.unmonitored = unmonitored;
10893
+ query.includeArtist = true;
10847
10894
  return getApiV1Calendar(Object.keys(query).length > 0 ? { query } : {});
10848
10895
  }
10849
10896
  async getCalendarFeed(pastDays, futureDays, tags) {
@@ -11201,6 +11248,34 @@ class LidarrClient {
11201
11248
  async removeBlocklistItemsBulk(ids) {
11202
11249
  return deleteApiV1BlocklistBulk({ body: { ids } });
11203
11250
  }
11251
+ async getWantedMissing(page, pageSize, sortKey, sortDirection, monitored) {
11252
+ const query = { includeArtist: true };
11253
+ if (page !== undefined)
11254
+ query.page = page;
11255
+ if (pageSize !== undefined)
11256
+ query.pageSize = pageSize;
11257
+ if (sortKey)
11258
+ query.sortKey = sortKey;
11259
+ if (sortDirection)
11260
+ query.sortDirection = sortDirection;
11261
+ if (monitored !== undefined)
11262
+ query.monitored = monitored;
11263
+ return getApiV1WantedMissing(Object.keys(query).length > 0 ? { query } : {});
11264
+ }
11265
+ async getWantedCutoff(page, pageSize, sortKey, sortDirection, monitored) {
11266
+ const query = { includeArtist: true };
11267
+ if (page !== undefined)
11268
+ query.page = page;
11269
+ if (pageSize !== undefined)
11270
+ query.pageSize = pageSize;
11271
+ if (sortKey)
11272
+ query.sortKey = sortKey;
11273
+ if (sortDirection)
11274
+ query.sortDirection = sortDirection;
11275
+ if (monitored !== undefined)
11276
+ query.monitored = monitored;
11277
+ return getApiV1WantedCutoff(Object.keys(query).length > 0 ? { query } : {});
11278
+ }
11204
11279
  updateConfig(newConfig) {
11205
11280
  const updatedConfig = { ...this.clientConfig.config, ...newConfig };
11206
11281
  this.clientConfig = createServarrClient(updatedConfig);
@@ -11222,6 +11297,38 @@ var exports_lidarr3 = {};
11222
11297
  __export(exports_lidarr3, {
11223
11298
  lidarr: () => lidarr
11224
11299
  });
11300
+ import { readFileSync as readFileSync4 } from "node:fs";
11301
+ function unwrapData3(result) {
11302
+ return result?.data ?? result;
11303
+ }
11304
+ function formatAlbumListItem(album) {
11305
+ return {
11306
+ ...album,
11307
+ artistName: album?.artistName ?? album?.artist?.artistName ?? "—"
11308
+ };
11309
+ }
11310
+ function formatQueueListItem(item) {
11311
+ return {
11312
+ ...item,
11313
+ artistName: item?.artistName ?? item?.artist?.artistName ?? "—"
11314
+ };
11315
+ }
11316
+ function formatHistoryListItem(item) {
11317
+ return {
11318
+ ...item,
11319
+ artistName: item?.artistName ?? item?.artist?.artistName ?? "—"
11320
+ };
11321
+ }
11322
+ function formatBlocklistItem(item) {
11323
+ return {
11324
+ ...item,
11325
+ artistName: item?.artistName ?? item?.artist?.artistName ?? "—"
11326
+ };
11327
+ }
11328
+ function readJsonInput3(filePath) {
11329
+ const raw = filePath === "-" ? readFileSync4(0, "utf-8") : readFileSync4(filePath, "utf-8");
11330
+ return JSON.parse(raw);
11331
+ }
11225
11332
  var resources3, lidarr;
11226
11333
  var init_lidarr3 = __esm(() => {
11227
11334
  init_lidarr2();
@@ -11257,31 +11364,28 @@ var init_lidarr3 = __esm(() => {
11257
11364
  description: "Search and add an artist",
11258
11365
  args: [{ name: "term", description: "Search term", required: true }],
11259
11366
  run: async (c3, a2) => {
11260
- const searchResult = await c3.searchArtists(a2.term);
11261
- const results = searchResult?.data ?? searchResult;
11367
+ const results = unwrapData3(await c3.searchArtists(a2.term));
11262
11368
  if (!Array.isArray(results) || results.length === 0) {
11263
11369
  throw new Error("No artists found.");
11264
11370
  }
11265
- const artistId = await promptSelect("Select an artist:", results.map((ar) => ({
11266
- label: ar.artistName,
11267
- value: String(ar.foreignArtistId)
11371
+ const artistId = await promptSelect("Select an artist:", results.map((artist2) => ({
11372
+ label: artist2.artistName,
11373
+ value: String(artist2.foreignArtistId)
11268
11374
  })));
11269
- const artist = results.find((ar) => String(ar.foreignArtistId) === artistId);
11375
+ const artist = results.find((item) => String(item.foreignArtistId) === artistId);
11270
11376
  if (!artist) {
11271
11377
  throw new Error("Selected artist was not found in the search results.");
11272
11378
  }
11273
- const profilesResult = await c3.getQualityProfiles();
11274
- const profiles = profilesResult?.data ?? profilesResult;
11379
+ const profiles = unwrapData3(await c3.getQualityProfiles());
11275
11380
  if (!Array.isArray(profiles) || profiles.length === 0) {
11276
11381
  throw new Error("No quality profiles found. Configure one in Lidarr first.");
11277
11382
  }
11278
- const profileId = await promptSelect("Select quality profile:", profiles.map((p) => ({ label: p.name, value: String(p.id) })));
11279
- const foldersResult = await c3.getRootFolders();
11280
- const folders = foldersResult?.data ?? foldersResult;
11383
+ const profileId = await promptSelect("Select quality profile:", profiles.map((profile) => ({ label: profile.name, value: String(profile.id) })));
11384
+ const folders = unwrapData3(await c3.getRootFolders());
11281
11385
  if (!Array.isArray(folders) || folders.length === 0) {
11282
11386
  throw new Error("No root folders found. Configure one in Lidarr first.");
11283
11387
  }
11284
- const rootFolderPath = await promptSelect("Select root folder:", folders.map((f3) => ({ label: f3.path, value: f3.path })));
11388
+ const rootFolderPath = await promptSelect("Select root folder:", folders.map((folder) => ({ label: folder.path, value: folder.path })));
11285
11389
  const confirmed = await promptConfirm(`Add "${artist.artistName}"?`, !!a2.yes);
11286
11390
  if (!confirmed)
11287
11391
  throw new Error("Cancelled.");
@@ -11304,15 +11408,16 @@ var init_lidarr3 = __esm(() => {
11304
11408
  { name: "tags", description: "Comma-separated tag IDs" }
11305
11409
  ],
11306
11410
  run: async (c3, a2) => {
11307
- const result = await c3.getArtist(a2.id);
11308
- const artist = result?.data ?? result;
11411
+ const artist = unwrapData3(await c3.getArtist(a2.id));
11309
11412
  const updates = { ...artist };
11310
11413
  if (a2.monitored !== undefined)
11311
11414
  updates.monitored = a2.monitored === "true";
11312
- if (a2["quality-profile-id"] !== undefined)
11415
+ if (a2["quality-profile-id"] !== undefined) {
11313
11416
  updates.qualityProfileId = Number(a2["quality-profile-id"]);
11314
- if (a2.tags !== undefined)
11315
- updates.tags = a2.tags.split(",").map((t2) => Number(t2.trim()));
11417
+ }
11418
+ if (a2.tags !== undefined) {
11419
+ updates.tags = a2.tags.split(",").map((tag) => Number(tag.trim()));
11420
+ }
11316
11421
  return c3.updateArtist(a2.id, updates);
11317
11422
  }
11318
11423
  },
@@ -11320,12 +11425,14 @@ var init_lidarr3 = __esm(() => {
11320
11425
  name: "refresh",
11321
11426
  description: "Refresh artist metadata",
11322
11427
  args: [{ name: "id", description: "Artist ID", required: true, type: "number" }],
11428
+ columns: COMMAND_OUTPUT_COLUMNS,
11323
11429
  run: (c3, a2) => c3.runCommand({ name: "RefreshArtist", artistId: a2.id })
11324
11430
  },
11325
11431
  {
11326
11432
  name: "manual-search",
11327
11433
  description: "Trigger a manual search for releases",
11328
11434
  args: [{ name: "id", description: "Artist ID", required: true, type: "number" }],
11435
+ columns: COMMAND_OUTPUT_COLUMNS,
11329
11436
  run: (c3, a2) => c3.runCommand({ name: "ArtistSearch", artistId: a2.id })
11330
11437
  },
11331
11438
  {
@@ -11344,22 +11451,56 @@ var init_lidarr3 = __esm(() => {
11344
11451
  {
11345
11452
  name: "list",
11346
11453
  description: "List all albums",
11347
- columns: ["id", "title", "artistId", "monitored"],
11348
- run: (c3) => c3.getAlbums()
11454
+ columns: ["id", "artistName", "title", "monitored"],
11455
+ run: async (c3) => {
11456
+ const albums = unwrapData3(await c3.getAlbums());
11457
+ return albums.map(formatAlbumListItem);
11458
+ }
11349
11459
  },
11350
11460
  {
11351
11461
  name: "get",
11352
11462
  description: "Get an album by ID",
11353
11463
  args: [{ name: "id", description: "Album ID", required: true, type: "number" }],
11354
- run: (c3, a2) => c3.getAlbum(a2.id)
11464
+ run: async (c3, a2) => {
11465
+ const album = unwrapData3(await c3.getAlbum(a2.id));
11466
+ return formatAlbumListItem(album);
11467
+ }
11355
11468
  },
11356
11469
  {
11357
11470
  name: "search",
11358
11471
  description: "Search for albums",
11359
11472
  args: [{ name: "term", description: "Search term", required: true }],
11360
- columns: ["foreignAlbumId", "title", "artistId"],
11473
+ columns: ["foreignAlbumId", "artistName", "title", "monitored"],
11361
11474
  idField: "foreignAlbumId",
11362
- run: (c3, a2) => c3.searchAlbums(a2.term)
11475
+ run: async (c3, a2) => {
11476
+ const albums = unwrapData3(await c3.searchAlbums(a2.term));
11477
+ return albums.map(formatAlbumListItem);
11478
+ }
11479
+ },
11480
+ {
11481
+ name: "add",
11482
+ description: "Add an album from JSON file or stdin",
11483
+ args: [{ name: "file", description: "JSON file path (use - for stdin)", required: true }],
11484
+ run: async (c3, a2) => c3.addAlbum(readJsonInput3(a2.file))
11485
+ },
11486
+ {
11487
+ name: "edit",
11488
+ description: "Edit an album (merges JSON with existing)",
11489
+ args: [
11490
+ { name: "id", description: "Album ID", required: true, type: "number" },
11491
+ { name: "file", description: "JSON file with fields to update", required: true }
11492
+ ],
11493
+ run: async (c3, a2) => {
11494
+ const existing = unwrapData3(await c3.getAlbum(a2.id));
11495
+ return c3.updateAlbum(a2.id, { ...existing, ...readJsonInput3(a2.file) });
11496
+ }
11497
+ },
11498
+ {
11499
+ name: "delete",
11500
+ description: "Delete an album",
11501
+ args: [{ name: "id", description: "Album ID", required: true, type: "number" }],
11502
+ confirmMessage: "Are you sure you want to delete this album?",
11503
+ run: (c3, a2) => c3.deleteAlbum(a2.id)
11363
11504
  }
11364
11505
  ]
11365
11506
  },
@@ -11372,6 +11513,12 @@ var init_lidarr3 = __esm(() => {
11372
11513
  description: "List quality profiles",
11373
11514
  columns: ["id", "name"],
11374
11515
  run: (c3) => c3.getQualityProfiles()
11516
+ },
11517
+ {
11518
+ name: "get",
11519
+ description: "Get a quality profile by ID",
11520
+ args: [{ name: "id", description: "Profile ID", required: true, type: "number" }],
11521
+ run: (c3, a2) => c3.getQualityProfile(a2.id)
11375
11522
  }
11376
11523
  ]
11377
11524
  },
@@ -11394,7 +11541,7 @@ var init_lidarr3 = __esm(() => {
11394
11541
  const tagResult = await c3.getTag(a2.id);
11395
11542
  if (tagResult?.error)
11396
11543
  return tagResult;
11397
- const tag = tagResult?.data ?? tagResult;
11544
+ const tag = unwrapData3(tagResult);
11398
11545
  const deleteResult = await c3.deleteTag(a2.id);
11399
11546
  if (deleteResult?.error)
11400
11547
  return deleteResult;
@@ -11410,84 +11557,332 @@ var init_lidarr3 = __esm(() => {
11410
11557
  ]
11411
11558
  },
11412
11559
  {
11413
- name: "rootfolder",
11414
- description: "Manage root folders",
11560
+ name: "queue",
11561
+ description: "Manage download queue",
11415
11562
  actions: [
11416
11563
  {
11417
11564
  name: "list",
11418
- description: "List root folders",
11419
- columns: ["id", "path", "freeSpace"],
11420
- run: (c3) => c3.getRootFolders()
11565
+ description: "List queue items",
11566
+ columns: ["id", "artistName", "title", "status", "sizeleft", "timeleft"],
11567
+ run: async (c3) => {
11568
+ const items = unwrapData3(await c3.getQueue());
11569
+ return items.map(formatQueueListItem);
11570
+ }
11421
11571
  },
11422
11572
  {
11423
- name: "add",
11424
- description: "Add a root folder",
11425
- args: [{ name: "path", description: "Folder path", required: true }],
11426
- run: (c3, a2) => c3.addRootFolder(a2.path)
11573
+ name: "status",
11574
+ description: "Get queue status",
11575
+ run: (c3) => c3.getQueueStatus()
11427
11576
  },
11428
11577
  {
11429
11578
  name: "delete",
11430
- description: "Delete a root folder",
11431
- args: [{ name: "id", description: "Root folder ID", required: true, type: "number" }],
11432
- confirmMessage: "Are you sure you want to delete this root folder?",
11433
- run: (c3, a2) => c3.deleteRootFolder(a2.id)
11579
+ description: "Remove an item from the queue",
11580
+ args: [
11581
+ { name: "id", description: "Queue item ID", required: true, type: "number" },
11582
+ { name: "blocklist", description: "Add to blocklist", type: "boolean" },
11583
+ {
11584
+ name: "remove-from-client",
11585
+ description: "Remove from download client",
11586
+ type: "boolean"
11587
+ }
11588
+ ],
11589
+ confirmMessage: "Are you sure you want to remove this queue item?",
11590
+ run: (c3, a2) => c3.removeQueueItem(a2.id, a2["remove-from-client"], a2.blocklist)
11591
+ },
11592
+ {
11593
+ name: "grab",
11594
+ description: "Force download a queue item",
11595
+ args: [{ name: "id", description: "Queue item ID", required: true, type: "number" }],
11596
+ run: (c3, a2) => c3.grabQueueItem(a2.id)
11434
11597
  }
11435
11598
  ]
11436
11599
  },
11437
11600
  {
11438
- name: "system",
11439
- description: "System information",
11601
+ name: "history",
11602
+ description: "View history",
11440
11603
  actions: [
11441
11604
  {
11442
- name: "status",
11443
- description: "Get system status",
11444
- run: (c3) => c3.getSystemStatus()
11605
+ name: "list",
11606
+ description: "List recent history",
11607
+ args: [
11608
+ { name: "since", description: "Start date (ISO 8601, e.g. 2024-01-01)" },
11609
+ { name: "until", description: "End date (ISO 8601, e.g. 2024-12-31)" }
11610
+ ],
11611
+ columns: ["id", "artistName", "sourceTitle", "eventType", "date"],
11612
+ run: async (c3, a2) => {
11613
+ const items = a2.since ? unwrapData3(await c3.getHistorySince(a2.since)) : unwrapData3(await c3.getHistory());
11614
+ const filtered = a2.until ? items.filter((item) => new Date(item.date) <= new Date(a2.until)) : items;
11615
+ return filtered.map(formatHistoryListItem);
11616
+ }
11617
+ }
11618
+ ]
11619
+ },
11620
+ {
11621
+ name: "calendar",
11622
+ description: "View upcoming releases",
11623
+ actions: [
11624
+ {
11625
+ name: "list",
11626
+ description: "List upcoming album releases",
11627
+ args: [
11628
+ { name: "start", description: "Start date (ISO 8601)" },
11629
+ { name: "end", description: "End date (ISO 8601)" },
11630
+ { name: "unmonitored", description: "Include unmonitored", type: "boolean" }
11631
+ ],
11632
+ columns: ["id", "artistName", "title", "releaseDate"],
11633
+ run: async (c3, a2) => {
11634
+ const albums = unwrapData3(await c3.getCalendar(a2.start, a2.end, a2.unmonitored));
11635
+ return albums.map(formatAlbumListItem);
11636
+ }
11637
+ }
11638
+ ]
11639
+ },
11640
+ {
11641
+ name: "notification",
11642
+ description: "Manage notifications",
11643
+ actions: [
11644
+ {
11645
+ name: "list",
11646
+ description: "List notification providers",
11647
+ columns: ["id", "name", "implementation"],
11648
+ run: (c3) => c3.getNotifications()
11445
11649
  },
11446
11650
  {
11447
- name: "health",
11448
- description: "Get health check results",
11449
- columns: ["type", "message", "source"],
11450
- run: (c3) => c3.getHealth()
11651
+ name: "get",
11652
+ description: "Get a notification by ID",
11653
+ args: [{ name: "id", description: "Notification ID", required: true, type: "number" }],
11654
+ run: (c3, a2) => c3.getNotification(a2.id)
11655
+ },
11656
+ {
11657
+ name: "add",
11658
+ description: "Add a notification from JSON file or stdin",
11659
+ args: [{ name: "file", description: "JSON file path (use - for stdin)", required: true }],
11660
+ run: async (c3, a2) => c3.addNotification(readJsonInput3(a2.file))
11661
+ },
11662
+ {
11663
+ name: "edit",
11664
+ description: "Edit a notification (merges JSON with existing)",
11665
+ args: [
11666
+ { name: "id", description: "Notification ID", required: true, type: "number" },
11667
+ { name: "file", description: "JSON file with fields to update", required: true }
11668
+ ],
11669
+ run: async (c3, a2) => {
11670
+ const existing = unwrapData3(await c3.getNotification(a2.id));
11671
+ return c3.updateNotification(a2.id, { ...existing, ...readJsonInput3(a2.file) });
11672
+ }
11673
+ },
11674
+ {
11675
+ name: "delete",
11676
+ description: "Delete a notification",
11677
+ args: [{ name: "id", description: "Notification ID", required: true, type: "number" }],
11678
+ confirmMessage: "Are you sure you want to delete this notification?",
11679
+ run: (c3, a2) => c3.deleteNotification(a2.id)
11680
+ },
11681
+ {
11682
+ name: "test",
11683
+ description: "Test all notifications",
11684
+ run: (c3) => c3.testAllNotifications()
11451
11685
  }
11452
11686
  ]
11453
- }
11454
- ];
11455
- lidarr = buildServiceCommand("lidarr", "Manage Lidarr (Music)", (config) => new LidarrClient(config), resources3);
11456
- });
11457
-
11458
- // src/generated/readarr/core/bodySerializer.gen.ts
11459
- var jsonBodySerializer4;
11460
- var init_bodySerializer_gen4 = __esm(() => {
11461
- jsonBodySerializer4 = {
11462
- bodySerializer: (body) => JSON.stringify(body, (_key, value) => typeof value === "bigint" ? value.toString() : value)
11463
- };
11464
- });
11465
-
11466
- // src/generated/readarr/core/serverSentEvents.gen.ts
11467
- var createSseClient4 = ({
11468
- onRequest,
11469
- onSseError,
11470
- onSseEvent,
11471
- responseTransformer,
11472
- responseValidator,
11473
- sseDefaultRetryDelay,
11474
- sseMaxRetryAttempts,
11475
- sseMaxRetryDelay,
11476
- sseSleepFn,
11477
- url,
11478
- ...options
11479
- }) => {
11480
- let lastEventId;
11481
- const sleep = sseSleepFn ?? ((ms) => new Promise((resolve2) => setTimeout(resolve2, ms)));
11482
- const createStream = async function* () {
11483
- let retryDelay = sseDefaultRetryDelay ?? 3000;
11484
- let attempt = 0;
11485
- const signal = options.signal ?? new AbortController().signal;
11486
- while (true) {
11487
- if (signal.aborted)
11488
- break;
11489
- attempt++;
11490
- const headers = options.headers instanceof Headers ? options.headers : new Headers(options.headers);
11687
+ },
11688
+ {
11689
+ name: "downloadclient",
11690
+ description: "Manage download clients",
11691
+ actions: [
11692
+ {
11693
+ name: "list",
11694
+ description: "List download clients",
11695
+ columns: ["id", "name", "implementation", "enable"],
11696
+ run: (c3) => c3.getDownloadClients()
11697
+ },
11698
+ {
11699
+ name: "get",
11700
+ description: "Get a download client by ID",
11701
+ args: [{ name: "id", description: "Download client ID", required: true, type: "number" }],
11702
+ run: (c3, a2) => c3.getDownloadClient(a2.id)
11703
+ },
11704
+ {
11705
+ name: "add",
11706
+ description: "Add a download client from JSON file or stdin",
11707
+ args: [{ name: "file", description: "JSON file path (use - for stdin)", required: true }],
11708
+ run: async (c3, a2) => c3.addDownloadClient(readJsonInput3(a2.file))
11709
+ },
11710
+ {
11711
+ name: "edit",
11712
+ description: "Edit a download client (merges JSON with existing)",
11713
+ args: [
11714
+ { name: "id", description: "Download client ID", required: true, type: "number" },
11715
+ { name: "file", description: "JSON file with fields to update", required: true }
11716
+ ],
11717
+ run: async (c3, a2) => {
11718
+ const existing = unwrapData3(await c3.getDownloadClient(a2.id));
11719
+ return c3.updateDownloadClient(a2.id, { ...existing, ...readJsonInput3(a2.file) });
11720
+ }
11721
+ },
11722
+ {
11723
+ name: "delete",
11724
+ description: "Delete a download client",
11725
+ args: [{ name: "id", description: "Download client ID", required: true, type: "number" }],
11726
+ confirmMessage: "Are you sure you want to delete this download client?",
11727
+ run: (c3, a2) => c3.deleteDownloadClient(a2.id)
11728
+ },
11729
+ {
11730
+ name: "test",
11731
+ description: "Test all download clients",
11732
+ run: (c3) => c3.testAllDownloadClients()
11733
+ }
11734
+ ]
11735
+ },
11736
+ {
11737
+ name: "blocklist",
11738
+ description: "View blocked releases",
11739
+ actions: [
11740
+ {
11741
+ name: "list",
11742
+ description: "List blocked releases",
11743
+ columns: ["id", "artistName", "sourceTitle", "date"],
11744
+ run: async (c3) => {
11745
+ const items = unwrapData3(await c3.getBlocklist());
11746
+ return items.map(formatBlocklistItem);
11747
+ }
11748
+ },
11749
+ {
11750
+ name: "delete",
11751
+ description: "Remove a release from the blocklist",
11752
+ args: [{ name: "id", description: "Blocklist item ID", required: true, type: "number" }],
11753
+ confirmMessage: "Are you sure you want to remove this blocklist entry?",
11754
+ run: (c3, a2) => c3.removeBlocklistItem(a2.id)
11755
+ }
11756
+ ]
11757
+ },
11758
+ {
11759
+ name: "wanted",
11760
+ description: "View missing and cutoff unmet albums",
11761
+ actions: [
11762
+ {
11763
+ name: "missing",
11764
+ description: "List albums with missing tracks",
11765
+ columns: ["id", "artistName", "title", "releaseDate"],
11766
+ run: async (c3) => {
11767
+ const albums = unwrapData3(await c3.getWantedMissing());
11768
+ return albums.map(formatAlbumListItem);
11769
+ }
11770
+ },
11771
+ {
11772
+ name: "cutoff",
11773
+ description: "List albums below quality cutoff",
11774
+ columns: ["id", "artistName", "title", "releaseDate"],
11775
+ run: async (c3) => {
11776
+ const albums = unwrapData3(await c3.getWantedCutoff());
11777
+ return albums.map(formatAlbumListItem);
11778
+ }
11779
+ }
11780
+ ]
11781
+ },
11782
+ {
11783
+ name: "importlist",
11784
+ description: "Manage import lists",
11785
+ actions: [
11786
+ {
11787
+ name: "list",
11788
+ description: "List import lists",
11789
+ columns: ["id", "name", "implementation", "enable"],
11790
+ run: (c3) => c3.getImportLists()
11791
+ },
11792
+ {
11793
+ name: "get",
11794
+ description: "Get an import list by ID",
11795
+ args: [{ name: "id", description: "Import list ID", required: true, type: "number" }],
11796
+ run: (c3, a2) => c3.getImportList(a2.id)
11797
+ },
11798
+ {
11799
+ name: "delete",
11800
+ description: "Delete an import list",
11801
+ args: [{ name: "id", description: "Import list ID", required: true, type: "number" }],
11802
+ confirmMessage: "Are you sure you want to delete this import list?",
11803
+ run: (c3, a2) => c3.deleteImportList(a2.id)
11804
+ }
11805
+ ]
11806
+ },
11807
+ {
11808
+ name: "rootfolder",
11809
+ description: "Manage root folders",
11810
+ actions: [
11811
+ {
11812
+ name: "list",
11813
+ description: "List root folders",
11814
+ columns: ["id", "path", "freeSpace"],
11815
+ run: (c3) => c3.getRootFolders()
11816
+ },
11817
+ {
11818
+ name: "add",
11819
+ description: "Add a root folder",
11820
+ args: [{ name: "path", description: "Folder path", required: true }],
11821
+ run: (c3, a2) => c3.addRootFolder(a2.path)
11822
+ },
11823
+ {
11824
+ name: "delete",
11825
+ description: "Delete a root folder",
11826
+ args: [{ name: "id", description: "Root folder ID", required: true, type: "number" }],
11827
+ confirmMessage: "Are you sure you want to delete this root folder?",
11828
+ run: (c3, a2) => c3.deleteRootFolder(a2.id)
11829
+ }
11830
+ ]
11831
+ },
11832
+ {
11833
+ name: "system",
11834
+ description: "System information",
11835
+ actions: [
11836
+ {
11837
+ name: "status",
11838
+ description: "Get system status",
11839
+ run: (c3) => c3.getSystemStatus()
11840
+ },
11841
+ {
11842
+ name: "health",
11843
+ description: "Get health check results",
11844
+ columns: ["type", "message", "source"],
11845
+ run: (c3) => c3.getHealth()
11846
+ }
11847
+ ]
11848
+ }
11849
+ ];
11850
+ lidarr = buildServiceCommand("lidarr", "Manage Lidarr (Music)", (config) => new LidarrClient(config), resources3);
11851
+ });
11852
+
11853
+ // src/generated/readarr/core/bodySerializer.gen.ts
11854
+ var jsonBodySerializer4;
11855
+ var init_bodySerializer_gen4 = __esm(() => {
11856
+ jsonBodySerializer4 = {
11857
+ bodySerializer: (body) => JSON.stringify(body, (_key, value) => typeof value === "bigint" ? value.toString() : value)
11858
+ };
11859
+ });
11860
+
11861
+ // src/generated/readarr/core/serverSentEvents.gen.ts
11862
+ var createSseClient4 = ({
11863
+ onRequest,
11864
+ onSseError,
11865
+ onSseEvent,
11866
+ responseTransformer,
11867
+ responseValidator,
11868
+ sseDefaultRetryDelay,
11869
+ sseMaxRetryAttempts,
11870
+ sseMaxRetryDelay,
11871
+ sseSleepFn,
11872
+ url,
11873
+ ...options
11874
+ }) => {
11875
+ let lastEventId;
11876
+ const sleep = sseSleepFn ?? ((ms) => new Promise((resolve2) => setTimeout(resolve2, ms)));
11877
+ const createStream = async function* () {
11878
+ let retryDelay = sseDefaultRetryDelay ?? 3000;
11879
+ let attempt = 0;
11880
+ const signal = options.signal ?? new AbortController().signal;
11881
+ while (true) {
11882
+ if (signal.aborted)
11883
+ break;
11884
+ attempt++;
11885
+ const headers = options.headers instanceof Headers ? options.headers : new Headers(options.headers);
11491
11886
  if (lastEventId !== undefined) {
11492
11887
  headers.set("Last-Event-ID", lastEventId);
11493
11888
  }
@@ -12428,6 +12823,22 @@ var getApiV1Author = (options) => (options?.client ?? client4).get({
12428
12823
  }],
12429
12824
  url: "/api/v1/book/lookup",
12430
12825
  ...options
12826
+ }), getApiV1Calendar2 = (options) => (options?.client ?? client4).get({
12827
+ security: [{ name: "X-Api-Key", type: "apiKey" }, {
12828
+ in: "query",
12829
+ name: "apikey",
12830
+ type: "apiKey"
12831
+ }],
12832
+ url: "/api/v1/calendar",
12833
+ ...options
12834
+ }), getFeedV1CalendarReadarrIcs = (options) => (options?.client ?? client4).get({
12835
+ security: [{ name: "X-Api-Key", type: "apiKey" }, {
12836
+ in: "query",
12837
+ name: "apikey",
12838
+ type: "apiKey"
12839
+ }],
12840
+ url: "/feed/v1/calendar/readarr.ics",
12841
+ ...options
12431
12842
  }), getApiV1Command2 = (options) => (options?.client ?? client4).get({
12432
12843
  security: [{ name: "X-Api-Key", type: "apiKey" }, {
12433
12844
  in: "query",
@@ -12504,6 +12915,14 @@ var getApiV1Author = (options) => (options?.client ?? client4).get({
12504
12915
  }],
12505
12916
  url: "/api/v1/customformat/schema",
12506
12917
  ...options
12918
+ }), getApiV1WantedCutoff2 = (options) => (options?.client ?? client4).get({
12919
+ security: [{ name: "X-Api-Key", type: "apiKey" }, {
12920
+ in: "query",
12921
+ name: "apikey",
12922
+ type: "apiKey"
12923
+ }],
12924
+ url: "/api/v1/wanted/cutoff",
12925
+ ...options
12507
12926
  }), getApiV1ConfigDevelopment = (options) => (options?.client ?? client4).get({
12508
12927
  security: [{ name: "X-Api-Key", type: "apiKey" }, {
12509
12928
  in: "query",
@@ -12916,6 +13335,14 @@ var getApiV1Author = (options) => (options?.client ?? client4).get({
12916
13335
  "Content-Type": "application/json",
12917
13336
  ...options.headers
12918
13337
  }
13338
+ }), getApiV1WantedMissing2 = (options) => (options?.client ?? client4).get({
13339
+ security: [{ name: "X-Api-Key", type: "apiKey" }, {
13340
+ in: "query",
13341
+ name: "apikey",
13342
+ type: "apiKey"
13343
+ }],
13344
+ url: "/api/v1/wanted/missing",
13345
+ ...options
12919
13346
  }), getApiV1ConfigNaming2 = (options) => (options?.client ?? client4).get({
12920
13347
  security: [{ name: "X-Api-Key", type: "apiKey" }, {
12921
13348
  in: "query",
@@ -13487,6 +13914,26 @@ class ReadarrClient {
13487
13914
  async searchBooks(term) {
13488
13915
  return getApiV1BookLookup({ query: { term } });
13489
13916
  }
13917
+ async getCalendar(start, end, unmonitored) {
13918
+ const query = { includeAuthor: true };
13919
+ if (start)
13920
+ query.start = start;
13921
+ if (end)
13922
+ query.end = end;
13923
+ if (unmonitored !== undefined)
13924
+ query.unmonitored = unmonitored;
13925
+ return getApiV1Calendar2(Object.keys(query).length > 0 ? { query } : {});
13926
+ }
13927
+ async getCalendarFeed(pastDays, futureDays, tagList) {
13928
+ const query = {};
13929
+ if (pastDays !== undefined)
13930
+ query.pastDays = pastDays;
13931
+ if (futureDays !== undefined)
13932
+ query.futureDays = futureDays;
13933
+ if (tagList)
13934
+ query.tagList = tagList;
13935
+ return getFeedV1CalendarReadarrIcs(Object.keys(query).length > 0 ? { query } : {});
13936
+ }
13490
13937
  async getQualityProfiles() {
13491
13938
  return getApiV1Qualityprofile2();
13492
13939
  }
@@ -13723,6 +14170,34 @@ class ReadarrClient {
13723
14170
  async removeBlocklistItemsBulk(ids) {
13724
14171
  return deleteApiV1BlocklistBulk2({ body: { ids } });
13725
14172
  }
14173
+ async getWantedMissing(page, pageSize, sortKey, sortDirection, monitored) {
14174
+ const query = { includeAuthor: true };
14175
+ if (page !== undefined)
14176
+ query.page = page;
14177
+ if (pageSize !== undefined)
14178
+ query.pageSize = pageSize;
14179
+ if (sortKey)
14180
+ query.sortKey = sortKey;
14181
+ if (sortDirection)
14182
+ query.sortDirection = sortDirection;
14183
+ if (monitored !== undefined)
14184
+ query.monitored = monitored;
14185
+ return getApiV1WantedMissing2(Object.keys(query).length > 0 ? { query } : {});
14186
+ }
14187
+ async getWantedCutoff(page, pageSize, sortKey, sortDirection, monitored) {
14188
+ const query = { includeAuthor: true };
14189
+ if (page !== undefined)
14190
+ query.page = page;
14191
+ if (pageSize !== undefined)
14192
+ query.pageSize = pageSize;
14193
+ if (sortKey)
14194
+ query.sortKey = sortKey;
14195
+ if (sortDirection)
14196
+ query.sortDirection = sortDirection;
14197
+ if (monitored !== undefined)
14198
+ query.monitored = monitored;
14199
+ return getApiV1WantedCutoff2(Object.keys(query).length > 0 ? { query } : {});
14200
+ }
13726
14201
  updateConfig(newConfig) {
13727
14202
  const updatedConfig = { ...this.clientConfig.config, ...newConfig };
13728
14203
  this.clientConfig = createServarrClient(updatedConfig);
@@ -13744,6 +14219,38 @@ var exports_readarr3 = {};
13744
14219
  __export(exports_readarr3, {
13745
14220
  readarr: () => readarr
13746
14221
  });
14222
+ import { readFileSync as readFileSync5 } from "node:fs";
14223
+ function unwrapData4(result) {
14224
+ return result?.data ?? result;
14225
+ }
14226
+ function formatBookListItem(book) {
14227
+ return {
14228
+ ...book,
14229
+ authorName: book?.authorName ?? book?.authorTitle ?? book?.author?.authorName ?? "—"
14230
+ };
14231
+ }
14232
+ function formatQueueListItem2(item) {
14233
+ return {
14234
+ ...item,
14235
+ authorName: item?.authorName ?? item?.authorTitle ?? item?.author?.authorName ?? "—"
14236
+ };
14237
+ }
14238
+ function formatHistoryListItem2(item) {
14239
+ return {
14240
+ ...item,
14241
+ authorName: item?.authorName ?? item?.authorTitle ?? item?.author?.authorName ?? "—"
14242
+ };
14243
+ }
14244
+ function formatBlocklistItem2(item) {
14245
+ return {
14246
+ ...item,
14247
+ authorName: item?.authorName ?? item?.authorTitle ?? item?.author?.authorName ?? "—"
14248
+ };
14249
+ }
14250
+ function readJsonInput4(filePath) {
14251
+ const raw = filePath === "-" ? readFileSync5(0, "utf-8") : readFileSync5(filePath, "utf-8");
14252
+ return JSON.parse(raw);
14253
+ }
13747
14254
  var resources4, readarr;
13748
14255
  var init_readarr3 = __esm(() => {
13749
14256
  init_readarr2();
@@ -13751,178 +14258,471 @@ var init_readarr3 = __esm(() => {
13751
14258
  init_service();
13752
14259
  resources4 = [
13753
14260
  {
13754
- name: "author",
13755
- description: "Manage authors",
14261
+ name: "author",
14262
+ description: "Manage authors",
14263
+ actions: [
14264
+ {
14265
+ name: "list",
14266
+ description: "List all authors",
14267
+ columns: ["id", "authorName", "monitored", "qualityProfileId"],
14268
+ run: (c3) => c3.getAuthors()
14269
+ },
14270
+ {
14271
+ name: "get",
14272
+ description: "Get an author by ID",
14273
+ args: [{ name: "id", description: "Author ID", required: true, type: "number" }],
14274
+ run: (c3, a2) => c3.getAuthor(a2.id)
14275
+ },
14276
+ {
14277
+ name: "search",
14278
+ description: "Search for authors",
14279
+ args: [{ name: "term", description: "Search term", required: true }],
14280
+ columns: ["foreignAuthorId", "authorName", "overview"],
14281
+ idField: "foreignAuthorId",
14282
+ run: (c3, a2) => c3.searchAuthors(a2.term)
14283
+ },
14284
+ {
14285
+ name: "add",
14286
+ description: "Search and add an author",
14287
+ args: [{ name: "term", description: "Search term", required: true }],
14288
+ run: async (c3, a2) => {
14289
+ const results = unwrapData4(await c3.searchAuthors(a2.term));
14290
+ if (!Array.isArray(results) || results.length === 0) {
14291
+ throw new Error("No authors found.");
14292
+ }
14293
+ const authorId = await promptSelect("Select an author:", results.map((author2) => ({
14294
+ label: author2.authorName,
14295
+ value: String(author2.foreignAuthorId)
14296
+ })));
14297
+ const author = results.find((item) => String(item.foreignAuthorId) === authorId);
14298
+ if (!author) {
14299
+ throw new Error("Selected author was not found in the search results.");
14300
+ }
14301
+ const profiles = unwrapData4(await c3.getQualityProfiles());
14302
+ if (!Array.isArray(profiles) || profiles.length === 0) {
14303
+ throw new Error("No quality profiles found. Configure one in Readarr first.");
14304
+ }
14305
+ const profileId = await promptSelect("Select quality profile:", profiles.map((profile) => ({ label: profile.name, value: String(profile.id) })));
14306
+ const folders = unwrapData4(await c3.getRootFolders());
14307
+ if (!Array.isArray(folders) || folders.length === 0) {
14308
+ throw new Error("No root folders found. Configure one in Readarr first.");
14309
+ }
14310
+ const rootFolderPath = await promptSelect("Select root folder:", folders.map((folder) => ({ label: folder.path, value: folder.path })));
14311
+ const confirmed = await promptConfirm(`Add "${author.authorName}"?`, !!a2.yes);
14312
+ if (!confirmed)
14313
+ throw new Error("Cancelled.");
14314
+ return c3.addAuthor({
14315
+ ...author,
14316
+ qualityProfileId: Number(profileId),
14317
+ rootFolderPath,
14318
+ monitored: true,
14319
+ addOptions: { searchForMissingBooks: true }
14320
+ });
14321
+ }
14322
+ },
14323
+ {
14324
+ name: "edit",
14325
+ description: "Edit an author",
14326
+ args: [
14327
+ { name: "id", description: "Author ID", required: true, type: "number" },
14328
+ { name: "monitored", description: "Set monitored (true/false)" },
14329
+ { name: "quality-profile-id", description: "Quality profile ID", type: "number" },
14330
+ { name: "tags", description: "Comma-separated tag IDs" }
14331
+ ],
14332
+ run: async (c3, a2) => {
14333
+ const author = unwrapData4(await c3.getAuthor(a2.id));
14334
+ const updates = { ...author };
14335
+ if (a2.monitored !== undefined)
14336
+ updates.monitored = a2.monitored === "true";
14337
+ if (a2["quality-profile-id"] !== undefined) {
14338
+ updates.qualityProfileId = Number(a2["quality-profile-id"]);
14339
+ }
14340
+ if (a2.tags !== undefined) {
14341
+ updates.tags = a2.tags.split(",").map((tag) => Number(tag.trim()));
14342
+ }
14343
+ return c3.updateAuthor(a2.id, updates);
14344
+ }
14345
+ },
14346
+ {
14347
+ name: "refresh",
14348
+ description: "Refresh author metadata",
14349
+ args: [{ name: "id", description: "Author ID", required: true, type: "number" }],
14350
+ columns: COMMAND_OUTPUT_COLUMNS,
14351
+ run: (c3, a2) => c3.runCommand({ name: "RefreshAuthor", authorId: a2.id })
14352
+ },
14353
+ {
14354
+ name: "manual-search",
14355
+ description: "Trigger a manual search for releases",
14356
+ args: [{ name: "id", description: "Author ID", required: true, type: "number" }],
14357
+ columns: COMMAND_OUTPUT_COLUMNS,
14358
+ run: (c3, a2) => c3.runCommand({ name: "AuthorSearch", authorId: a2.id })
14359
+ },
14360
+ {
14361
+ name: "delete",
14362
+ description: "Delete an author",
14363
+ args: [{ name: "id", description: "Author ID", required: true, type: "number" }],
14364
+ confirmMessage: "Are you sure you want to delete this author?",
14365
+ run: (c3, a2) => c3.deleteAuthor(a2.id)
14366
+ }
14367
+ ]
14368
+ },
14369
+ {
14370
+ name: "book",
14371
+ description: "Manage books",
14372
+ actions: [
14373
+ {
14374
+ name: "list",
14375
+ description: "List all books",
14376
+ columns: ["id", "authorName", "title", "monitored"],
14377
+ run: async (c3) => {
14378
+ const books = unwrapData4(await c3.getBooks());
14379
+ return books.map(formatBookListItem);
14380
+ }
14381
+ },
14382
+ {
14383
+ name: "get",
14384
+ description: "Get a book by ID",
14385
+ args: [{ name: "id", description: "Book ID", required: true, type: "number" }],
14386
+ run: async (c3, a2) => {
14387
+ const book = unwrapData4(await c3.getBook(a2.id));
14388
+ return formatBookListItem(book);
14389
+ }
14390
+ },
14391
+ {
14392
+ name: "search",
14393
+ description: "Search for books",
14394
+ args: [{ name: "term", description: "Search term", required: true }],
14395
+ columns: ["foreignBookId", "authorName", "title", "monitored"],
14396
+ idField: "foreignBookId",
14397
+ run: async (c3, a2) => {
14398
+ const books = unwrapData4(await c3.searchBooks(a2.term));
14399
+ return books.map(formatBookListItem);
14400
+ }
14401
+ },
14402
+ {
14403
+ name: "add",
14404
+ description: "Add a book from JSON file or stdin",
14405
+ args: [{ name: "file", description: "JSON file path (use - for stdin)", required: true }],
14406
+ run: async (c3, a2) => c3.addBook(readJsonInput4(a2.file))
14407
+ },
14408
+ {
14409
+ name: "edit",
14410
+ description: "Edit a book (merges JSON with existing)",
14411
+ args: [
14412
+ { name: "id", description: "Book ID", required: true, type: "number" },
14413
+ { name: "file", description: "JSON file with fields to update", required: true }
14414
+ ],
14415
+ run: async (c3, a2) => {
14416
+ const existing = unwrapData4(await c3.getBook(a2.id));
14417
+ return c3.updateBook(a2.id, { ...existing, ...readJsonInput4(a2.file) });
14418
+ }
14419
+ },
14420
+ {
14421
+ name: "delete",
14422
+ description: "Delete a book",
14423
+ args: [{ name: "id", description: "Book ID", required: true, type: "number" }],
14424
+ confirmMessage: "Are you sure you want to delete this book?",
14425
+ run: (c3, a2) => c3.deleteBook(a2.id)
14426
+ }
14427
+ ]
14428
+ },
14429
+ {
14430
+ name: "profile",
14431
+ description: "Manage quality profiles",
14432
+ actions: [
14433
+ {
14434
+ name: "list",
14435
+ description: "List quality profiles",
14436
+ columns: ["id", "name"],
14437
+ run: (c3) => c3.getQualityProfiles()
14438
+ },
14439
+ {
14440
+ name: "get",
14441
+ description: "Get a quality profile by ID",
14442
+ args: [{ name: "id", description: "Profile ID", required: true, type: "number" }],
14443
+ run: (c3, a2) => c3.getQualityProfile(a2.id)
14444
+ }
14445
+ ]
14446
+ },
14447
+ {
14448
+ name: "tag",
14449
+ description: "Manage tags",
14450
+ actions: [
14451
+ {
14452
+ name: "create",
14453
+ description: "Create a tag",
14454
+ args: [{ name: "label", description: "Tag label", required: true }],
14455
+ run: (c3, a2) => c3.addTag({ label: a2.label })
14456
+ },
14457
+ {
14458
+ name: "delete",
14459
+ description: "Delete a tag",
14460
+ args: [{ name: "id", description: "Tag ID", required: true, type: "number" }],
14461
+ confirmMessage: "Are you sure you want to delete this tag?",
14462
+ run: async (c3, a2) => {
14463
+ const tagResult = await c3.getTag(a2.id);
14464
+ if (tagResult?.error)
14465
+ return tagResult;
14466
+ const tag = unwrapData4(tagResult);
14467
+ const deleteResult = await c3.deleteTag(a2.id);
14468
+ if (deleteResult?.error)
14469
+ return deleteResult;
14470
+ return { message: `Deleted tag: ${tag.label} (ID: ${tag.id})` };
14471
+ }
14472
+ },
14473
+ {
14474
+ name: "list",
14475
+ description: "List all tags",
14476
+ columns: ["id", "label"],
14477
+ run: (c3) => c3.getTags()
14478
+ }
14479
+ ]
14480
+ },
14481
+ {
14482
+ name: "queue",
14483
+ description: "Manage download queue",
14484
+ actions: [
14485
+ {
14486
+ name: "list",
14487
+ description: "List queue items",
14488
+ columns: ["id", "authorName", "title", "status", "sizeleft", "timeleft"],
14489
+ run: async (c3) => {
14490
+ const items = unwrapData4(await c3.getQueue());
14491
+ return items.map(formatQueueListItem2);
14492
+ }
14493
+ },
14494
+ {
14495
+ name: "status",
14496
+ description: "Get queue status",
14497
+ run: (c3) => c3.getQueueStatus()
14498
+ },
14499
+ {
14500
+ name: "delete",
14501
+ description: "Remove an item from the queue",
14502
+ args: [
14503
+ { name: "id", description: "Queue item ID", required: true, type: "number" },
14504
+ { name: "blocklist", description: "Add to blocklist", type: "boolean" },
14505
+ {
14506
+ name: "remove-from-client",
14507
+ description: "Remove from download client",
14508
+ type: "boolean"
14509
+ }
14510
+ ],
14511
+ confirmMessage: "Are you sure you want to remove this queue item?",
14512
+ run: (c3, a2) => c3.removeQueueItem(a2.id, a2["remove-from-client"], a2.blocklist)
14513
+ },
14514
+ {
14515
+ name: "grab",
14516
+ description: "Force download a queue item",
14517
+ args: [{ name: "id", description: "Queue item ID", required: true, type: "number" }],
14518
+ run: (c3, a2) => c3.grabQueueItem(a2.id)
14519
+ }
14520
+ ]
14521
+ },
14522
+ {
14523
+ name: "history",
14524
+ description: "View history",
14525
+ actions: [
14526
+ {
14527
+ name: "list",
14528
+ description: "List recent history",
14529
+ args: [
14530
+ { name: "since", description: "Start date (ISO 8601, e.g. 2024-01-01)" },
14531
+ { name: "until", description: "End date (ISO 8601, e.g. 2024-12-31)" }
14532
+ ],
14533
+ columns: ["id", "authorName", "sourceTitle", "eventType", "date"],
14534
+ run: async (c3, a2) => {
14535
+ const items = a2.since ? unwrapData4(await c3.getHistorySince(a2.since)) : unwrapData4(await c3.getHistory());
14536
+ const filtered = a2.until ? items.filter((item) => new Date(item.date) <= new Date(a2.until)) : items;
14537
+ return filtered.map(formatHistoryListItem2);
14538
+ }
14539
+ }
14540
+ ]
14541
+ },
14542
+ {
14543
+ name: "calendar",
14544
+ description: "View upcoming releases",
13756
14545
  actions: [
13757
14546
  {
13758
14547
  name: "list",
13759
- description: "List all authors",
13760
- columns: ["id", "authorName", "monitored", "qualityProfileId"],
13761
- run: (c3) => c3.getAuthors()
13762
- },
14548
+ description: "List upcoming book releases",
14549
+ args: [
14550
+ { name: "start", description: "Start date (ISO 8601)" },
14551
+ { name: "end", description: "End date (ISO 8601)" },
14552
+ { name: "unmonitored", description: "Include unmonitored", type: "boolean" }
14553
+ ],
14554
+ columns: ["id", "authorName", "title", "releaseDate"],
14555
+ run: async (c3, a2) => {
14556
+ const books = unwrapData4(await c3.getCalendar(a2.start, a2.end, a2.unmonitored));
14557
+ return books.map(formatBookListItem);
14558
+ }
14559
+ }
14560
+ ]
14561
+ },
14562
+ {
14563
+ name: "notification",
14564
+ description: "Manage notifications",
14565
+ actions: [
13763
14566
  {
13764
- name: "get",
13765
- description: "Get an author by ID",
13766
- args: [{ name: "id", description: "Author ID", required: true, type: "number" }],
13767
- run: (c3, a2) => c3.getAuthor(a2.id)
14567
+ name: "list",
14568
+ description: "List notification providers",
14569
+ columns: ["id", "name", "implementation"],
14570
+ run: (c3) => c3.getNotifications()
13768
14571
  },
13769
14572
  {
13770
- name: "search",
13771
- description: "Search for authors",
13772
- args: [{ name: "term", description: "Search term", required: true }],
13773
- columns: ["foreignAuthorId", "authorName", "overview"],
13774
- run: (c3, a2) => c3.searchAuthors(a2.term)
14573
+ name: "get",
14574
+ description: "Get a notification by ID",
14575
+ args: [{ name: "id", description: "Notification ID", required: true, type: "number" }],
14576
+ run: (c3, a2) => c3.getNotification(a2.id)
13775
14577
  },
13776
14578
  {
13777
14579
  name: "add",
13778
- description: "Search and add an author",
13779
- args: [{ name: "term", description: "Search term", required: true }],
13780
- run: async (c3, a2) => {
13781
- const searchResult = await c3.searchAuthors(a2.term);
13782
- const results = searchResult?.data ?? searchResult;
13783
- if (!Array.isArray(results) || results.length === 0) {
13784
- throw new Error("No authors found.");
13785
- }
13786
- const authorId = await promptSelect("Select an author:", results.map((au) => ({
13787
- label: au.authorName,
13788
- value: String(au.foreignAuthorId)
13789
- })));
13790
- const author = results.find((au) => String(au.foreignAuthorId) === authorId);
13791
- const profilesResult = await c3.getQualityProfiles();
13792
- const profiles = profilesResult?.data ?? profilesResult;
13793
- if (!Array.isArray(profiles) || profiles.length === 0) {
13794
- throw new Error("No quality profiles found. Configure one in Readarr first.");
13795
- }
13796
- const profileId = await promptSelect("Select quality profile:", profiles.map((p) => ({ label: p.name, value: String(p.id) })));
13797
- const foldersResult = await c3.getRootFolders();
13798
- const folders = foldersResult?.data ?? foldersResult;
13799
- if (!Array.isArray(folders) || folders.length === 0) {
13800
- throw new Error("No root folders found. Configure one in Readarr first.");
13801
- }
13802
- const rootFolderPath = await promptSelect("Select root folder:", folders.map((f3) => ({ label: f3.path, value: f3.path })));
13803
- const confirmed = await promptConfirm(`Add "${author.authorName}"?`, !!a2.yes);
13804
- if (!confirmed)
13805
- throw new Error("Cancelled.");
13806
- return c3.addAuthor({
13807
- ...author,
13808
- qualityProfileId: Number(profileId),
13809
- rootFolderPath,
13810
- monitored: true,
13811
- addOptions: { searchForMissingBooks: true }
13812
- });
13813
- }
14580
+ description: "Add a notification from JSON file or stdin",
14581
+ args: [{ name: "file", description: "JSON file path (use - for stdin)", required: true }],
14582
+ run: async (c3, a2) => c3.addNotification(readJsonInput4(a2.file))
13814
14583
  },
13815
14584
  {
13816
14585
  name: "edit",
13817
- description: "Edit an author",
14586
+ description: "Edit a notification (merges JSON with existing)",
13818
14587
  args: [
13819
- { name: "id", description: "Author ID", required: true, type: "number" },
13820
- { name: "monitored", description: "Set monitored (true/false)" },
13821
- { name: "quality-profile-id", description: "Quality profile ID", type: "number" },
13822
- { name: "tags", description: "Comma-separated tag IDs" }
14588
+ { name: "id", description: "Notification ID", required: true, type: "number" },
14589
+ { name: "file", description: "JSON file with fields to update", required: true }
13823
14590
  ],
13824
14591
  run: async (c3, a2) => {
13825
- const result = await c3.getAuthor(a2.id);
13826
- const author = result?.data ?? result;
13827
- const updates = { ...author };
13828
- if (a2.monitored !== undefined)
13829
- updates.monitored = a2.monitored === "true";
13830
- if (a2["quality-profile-id"] !== undefined)
13831
- updates.qualityProfileId = Number(a2["quality-profile-id"]);
13832
- if (a2.tags !== undefined)
13833
- updates.tags = a2.tags.split(",").map((t2) => Number(t2.trim()));
13834
- return c3.updateAuthor(a2.id, updates);
14592
+ const existing = unwrapData4(await c3.getNotification(a2.id));
14593
+ return c3.updateNotification(a2.id, { ...existing, ...readJsonInput4(a2.file) });
13835
14594
  }
13836
14595
  },
13837
14596
  {
13838
- name: "refresh",
13839
- description: "Refresh author metadata",
13840
- args: [{ name: "id", description: "Author ID", required: true, type: "number" }],
13841
- run: (c3, a2) => c3.runCommand({ name: "RefreshAuthor", authorId: a2.id })
13842
- },
13843
- {
13844
- name: "manual-search",
13845
- description: "Trigger a manual search for releases",
13846
- args: [{ name: "id", description: "Author ID", required: true, type: "number" }],
13847
- run: (c3, a2) => c3.runCommand({ name: "AuthorSearch", authorId: a2.id })
14597
+ name: "delete",
14598
+ description: "Delete a notification",
14599
+ args: [{ name: "id", description: "Notification ID", required: true, type: "number" }],
14600
+ confirmMessage: "Are you sure you want to delete this notification?",
14601
+ run: (c3, a2) => c3.deleteNotification(a2.id)
13848
14602
  },
13849
14603
  {
13850
- name: "delete",
13851
- description: "Delete an author",
13852
- args: [{ name: "id", description: "Author ID", required: true, type: "number" }],
13853
- confirmMessage: "Are you sure you want to delete this author?",
13854
- run: (c3, a2) => c3.deleteAuthor(a2.id)
14604
+ name: "test",
14605
+ description: "Test all notifications",
14606
+ run: (c3) => c3.testAllNotifications()
13855
14607
  }
13856
14608
  ]
13857
14609
  },
13858
14610
  {
13859
- name: "book",
13860
- description: "Manage books",
14611
+ name: "downloadclient",
14612
+ description: "Manage download clients",
13861
14613
  actions: [
13862
14614
  {
13863
14615
  name: "list",
13864
- description: "List all books",
13865
- columns: ["id", "title", "authorId", "monitored"],
13866
- run: (c3) => c3.getBooks()
14616
+ description: "List download clients",
14617
+ columns: ["id", "name", "implementation", "enable"],
14618
+ run: (c3) => c3.getDownloadClients()
13867
14619
  },
13868
14620
  {
13869
14621
  name: "get",
13870
- description: "Get a book by ID",
13871
- args: [{ name: "id", description: "Book ID", required: true, type: "number" }],
13872
- run: (c3, a2) => c3.getBook(a2.id)
14622
+ description: "Get a download client by ID",
14623
+ args: [{ name: "id", description: "Download client ID", required: true, type: "number" }],
14624
+ run: (c3, a2) => c3.getDownloadClient(a2.id)
13873
14625
  },
13874
14626
  {
13875
- name: "search",
13876
- description: "Search for books",
13877
- args: [{ name: "term", description: "Search term", required: true }],
13878
- columns: ["foreignBookId", "title", "authorId"],
13879
- run: (c3, a2) => c3.searchBooks(a2.term)
14627
+ name: "add",
14628
+ description: "Add a download client from JSON file or stdin",
14629
+ args: [{ name: "file", description: "JSON file path (use - for stdin)", required: true }],
14630
+ run: async (c3, a2) => c3.addDownloadClient(readJsonInput4(a2.file))
14631
+ },
14632
+ {
14633
+ name: "edit",
14634
+ description: "Edit a download client (merges JSON with existing)",
14635
+ args: [
14636
+ { name: "id", description: "Download client ID", required: true, type: "number" },
14637
+ { name: "file", description: "JSON file with fields to update", required: true }
14638
+ ],
14639
+ run: async (c3, a2) => {
14640
+ const existing = unwrapData4(await c3.getDownloadClient(a2.id));
14641
+ return c3.updateDownloadClient(a2.id, { ...existing, ...readJsonInput4(a2.file) });
14642
+ }
14643
+ },
14644
+ {
14645
+ name: "delete",
14646
+ description: "Delete a download client",
14647
+ args: [{ name: "id", description: "Download client ID", required: true, type: "number" }],
14648
+ confirmMessage: "Are you sure you want to delete this download client?",
14649
+ run: (c3, a2) => c3.deleteDownloadClient(a2.id)
14650
+ },
14651
+ {
14652
+ name: "test",
14653
+ description: "Test all download clients",
14654
+ run: (c3) => c3.testAllDownloadClients()
13880
14655
  }
13881
14656
  ]
13882
14657
  },
13883
14658
  {
13884
- name: "profile",
13885
- description: "Manage quality profiles",
14659
+ name: "blocklist",
14660
+ description: "View blocked releases",
13886
14661
  actions: [
13887
14662
  {
13888
14663
  name: "list",
13889
- description: "List quality profiles",
13890
- columns: ["id", "name"],
13891
- run: (c3) => c3.getQualityProfiles()
14664
+ description: "List blocked releases",
14665
+ columns: ["id", "authorName", "sourceTitle", "date"],
14666
+ run: async (c3) => {
14667
+ const items = unwrapData4(await c3.getBlocklist());
14668
+ return items.map(formatBlocklistItem2);
14669
+ }
14670
+ },
14671
+ {
14672
+ name: "delete",
14673
+ description: "Remove a release from the blocklist",
14674
+ args: [{ name: "id", description: "Blocklist item ID", required: true, type: "number" }],
14675
+ confirmMessage: "Are you sure you want to remove this blocklist entry?",
14676
+ run: (c3, a2) => c3.removeBlocklistItem(a2.id)
13892
14677
  }
13893
14678
  ]
13894
14679
  },
13895
14680
  {
13896
- name: "tag",
13897
- description: "Manage tags",
14681
+ name: "wanted",
14682
+ description: "View missing and cutoff unmet books",
13898
14683
  actions: [
13899
14684
  {
13900
- name: "create",
13901
- description: "Create a tag",
13902
- args: [{ name: "label", description: "Tag label", required: true }],
13903
- run: (c3, a2) => c3.addTag({ label: a2.label })
14685
+ name: "missing",
14686
+ description: "List books with missing files",
14687
+ columns: ["id", "authorName", "title", "releaseDate"],
14688
+ run: async (c3) => {
14689
+ const books = unwrapData4(await c3.getWantedMissing());
14690
+ return books.map(formatBookListItem);
14691
+ }
13904
14692
  },
13905
14693
  {
13906
- name: "delete",
13907
- description: "Delete a tag",
13908
- args: [{ name: "id", description: "Tag ID", required: true, type: "number" }],
13909
- confirmMessage: "Are you sure you want to delete this tag?",
13910
- run: async (c3, a2) => {
13911
- const tagResult = await c3.getTag(a2.id);
13912
- if (tagResult?.error)
13913
- return tagResult;
13914
- const tag = tagResult?.data ?? tagResult;
13915
- const deleteResult = await c3.deleteTag(a2.id);
13916
- if (deleteResult?.error)
13917
- return deleteResult;
13918
- return { message: `Deleted tag: ${tag.label} (ID: ${tag.id})` };
14694
+ name: "cutoff",
14695
+ description: "List books below quality cutoff",
14696
+ columns: ["id", "authorName", "title", "releaseDate"],
14697
+ run: async (c3) => {
14698
+ const books = unwrapData4(await c3.getWantedCutoff());
14699
+ return books.map(formatBookListItem);
13919
14700
  }
13920
- },
14701
+ }
14702
+ ]
14703
+ },
14704
+ {
14705
+ name: "importlist",
14706
+ description: "Manage import lists",
14707
+ actions: [
13921
14708
  {
13922
14709
  name: "list",
13923
- description: "List all tags",
13924
- columns: ["id", "label"],
13925
- run: (c3) => c3.getTags()
14710
+ description: "List import lists",
14711
+ columns: ["id", "name", "implementation", "enable"],
14712
+ run: (c3) => c3.getImportLists()
14713
+ },
14714
+ {
14715
+ name: "get",
14716
+ description: "Get an import list by ID",
14717
+ args: [{ name: "id", description: "Import list ID", required: true, type: "number" }],
14718
+ run: (c3, a2) => c3.getImportList(a2.id)
14719
+ },
14720
+ {
14721
+ name: "delete",
14722
+ description: "Delete an import list",
14723
+ args: [{ name: "id", description: "Import list ID", required: true, type: "number" }],
14724
+ confirmMessage: "Are you sure you want to delete this import list?",
14725
+ run: (c3, a2) => c3.deleteImportList(a2.id)
13926
14726
  }
13927
14727
  ]
13928
14728
  },
@@ -15592,14 +16392,53 @@ var exports_prowlarr3 = {};
15592
16392
  __export(exports_prowlarr3, {
15593
16393
  prowlarr: () => prowlarr
15594
16394
  });
15595
- import { readFileSync as readFileSync4 } from "node:fs";
15596
- function unwrapData3(result) {
16395
+ import { readFileSync as readFileSync6 } from "node:fs";
16396
+ function unwrapData5(result) {
15597
16397
  return result?.data ?? result;
15598
16398
  }
15599
- function readJsonInput3(filePath) {
15600
- const raw = filePath === "-" ? readFileSync4(0, "utf-8") : readFileSync4(filePath, "utf-8");
16399
+ function readJsonInput5(filePath) {
16400
+ const raw = filePath === "-" ? readFileSync6(0, "utf-8") : readFileSync6(filePath, "utf-8");
15601
16401
  return JSON.parse(raw);
15602
16402
  }
16403
+ async function runIndexerTest(client6, indexer) {
16404
+ const result = await client6.testIndexer(indexer);
16405
+ if (result?.error) {
16406
+ const error = result.error;
16407
+ const response = result.response;
16408
+ const status = error?.status ?? response?.status;
16409
+ return {
16410
+ id: indexer?.id,
16411
+ name: indexer?.name ?? "Unknown indexer",
16412
+ status: "fail",
16413
+ message: error?.title ?? error?.message ?? `API error (${status ?? "unknown"})`
16414
+ };
16415
+ }
16416
+ const data = unwrapData5(result);
16417
+ return {
16418
+ id: indexer?.id,
16419
+ name: indexer?.name ?? "Unknown indexer",
16420
+ status: "ok",
16421
+ message: extractIndexerTestMessage(data)
16422
+ };
16423
+ }
16424
+ function extractIndexerTestMessage(data) {
16425
+ if (typeof data === "string" && data.trim() !== "") {
16426
+ return data;
16427
+ }
16428
+ if (Array.isArray(data) && data.length > 0) {
16429
+ const first = data[0];
16430
+ if (typeof first === "string" && first.trim() !== "") {
16431
+ return first;
16432
+ }
16433
+ if (typeof first?.message === "string" && first.message.trim() !== "") {
16434
+ return first.message;
16435
+ }
16436
+ }
16437
+ if (typeof data?.message === "string" && data.message.trim() !== "") {
16438
+ return data.message;
16439
+ }
16440
+ return "Test completed";
16441
+ }
15603
16442
  var resources5, prowlarr;
15604
16443
  var init_prowlarr3 = __esm(() => {
15605
16444
  init_prowlarr2();
@@ -15627,7 +16466,7 @@ var init_prowlarr3 = __esm(() => {
15627
16466
  description: "Add an indexer from JSON file or stdin",
15628
16467
  args: [{ name: "file", description: "JSON file path (use - for stdin)", required: true }],
15629
16468
  run: async (c3, a2) => {
15630
- const body = readJsonInput3(a2.file);
16469
+ const body = readJsonInput5(a2.file);
15631
16470
  return c3.addIndexer(body);
15632
16471
  }
15633
16472
  },
@@ -15639,8 +16478,8 @@ var init_prowlarr3 = __esm(() => {
15639
16478
  { name: "file", description: "JSON file with fields to update", required: true }
15640
16479
  ],
15641
16480
  run: async (c3, a2) => {
15642
- const existing = unwrapData3(await c3.getIndexer(a2.id));
15643
- const updates = readJsonInput3(a2.file);
16481
+ const existing = unwrapData5(await c3.getIndexer(a2.id));
16482
+ const updates = readJsonInput5(a2.file);
15644
16483
  return c3.updateIndexer(a2.id, { ...existing, ...updates });
15645
16484
  }
15646
16485
  },
@@ -15653,8 +16492,17 @@ var init_prowlarr3 = __esm(() => {
15653
16492
  },
15654
16493
  {
15655
16494
  name: "test",
15656
- description: "Test all indexers",
15657
- run: (c3) => c3.testAllIndexers()
16495
+ description: "Test one indexer or all configured indexers",
16496
+ args: [{ name: "id", description: "Indexer ID", type: "number" }],
16497
+ columns: ["id", "name", "status", "message"],
16498
+ run: async (c3, a2) => {
16499
+ const indexers = a2.id ? [unwrapData5(await c3.getIndexer(a2.id))] : unwrapData5(await c3.getIndexers());
16500
+ const results = [];
16501
+ for (const indexer of indexers) {
16502
+ results.push(await runIndexerTest(c3, indexer));
16503
+ }
16504
+ return a2.id ? results[0] : results;
16505
+ }
15658
16506
  }
15659
16507
  ]
15660
16508
  },
@@ -15695,7 +16543,7 @@ var init_prowlarr3 = __esm(() => {
15695
16543
  description: "Add an application from JSON file or stdin",
15696
16544
  args: [{ name: "file", description: "JSON file path (use - for stdin)", required: true }],
15697
16545
  run: async (c3, a2) => {
15698
- const body = readJsonInput3(a2.file);
16546
+ const body = readJsonInput5(a2.file);
15699
16547
  return c3.addApplication(body);
15700
16548
  }
15701
16549
  },
@@ -15707,8 +16555,8 @@ var init_prowlarr3 = __esm(() => {
15707
16555
  { name: "file", description: "JSON file with fields to update", required: true }
15708
16556
  ],
15709
16557
  run: async (c3, a2) => {
15710
- const existing = unwrapData3(await c3.getApplication(a2.id));
15711
- const updates = readJsonInput3(a2.file);
16558
+ const existing = unwrapData5(await c3.getApplication(a2.id));
16559
+ const updates = readJsonInput5(a2.file);
15712
16560
  return c3.updateApplication(a2.id, { ...existing, ...updates });
15713
16561
  }
15714
16562
  },
@@ -15722,6 +16570,7 @@ var init_prowlarr3 = __esm(() => {
15722
16570
  {
15723
16571
  name: "sync",
15724
16572
  description: "Trigger app indexer sync",
16573
+ columns: COMMAND_OUTPUT_COLUMNS,
15725
16574
  run: (c3) => c3.runCommand({ name: "AppIndexerMapSync" })
15726
16575
  }
15727
16576
  ]
@@ -15775,7 +16624,7 @@ var init_prowlarr3 = __esm(() => {
15775
16624
  "averageResponseTime"
15776
16625
  ],
15777
16626
  run: async (c3) => {
15778
- const result = unwrapData3(await c3.getIndexerStats());
16627
+ const result = unwrapData5(await c3.getIndexerStats());
15779
16628
  return result?.indexers ?? result;
15780
16629
  }
15781
16630
  }
@@ -15801,7 +16650,7 @@ var init_prowlarr3 = __esm(() => {
15801
16650
  name: "add",
15802
16651
  description: "Add a notification from JSON file or stdin",
15803
16652
  args: [{ name: "file", description: "JSON file path (use - for stdin)", required: true }],
15804
- run: async (c3, a2) => c3.addNotification(readJsonInput3(a2.file))
16653
+ run: async (c3, a2) => c3.addNotification(readJsonInput5(a2.file))
15805
16654
  },
15806
16655
  {
15807
16656
  name: "edit",
@@ -15811,8 +16660,8 @@ var init_prowlarr3 = __esm(() => {
15811
16660
  { name: "file", description: "JSON file with fields to update", required: true }
15812
16661
  ],
15813
16662
  run: async (c3, a2) => {
15814
- const existing = unwrapData3(await c3.getNotification(a2.id));
15815
- return c3.updateNotification(a2.id, { ...existing, ...readJsonInput3(a2.file) });
16663
+ const existing = unwrapData5(await c3.getNotification(a2.id));
16664
+ return c3.updateNotification(a2.id, { ...existing, ...readJsonInput5(a2.file) });
15816
16665
  }
15817
16666
  },
15818
16667
  {
@@ -15849,7 +16698,7 @@ var init_prowlarr3 = __esm(() => {
15849
16698
  name: "add",
15850
16699
  description: "Add a download client from JSON file or stdin",
15851
16700
  args: [{ name: "file", description: "JSON file path (use - for stdin)", required: true }],
15852
- run: async (c3, a2) => c3.addDownloadClient(readJsonInput3(a2.file))
16701
+ run: async (c3, a2) => c3.addDownloadClient(readJsonInput5(a2.file))
15853
16702
  },
15854
16703
  {
15855
16704
  name: "edit",
@@ -15859,8 +16708,8 @@ var init_prowlarr3 = __esm(() => {
15859
16708
  { name: "file", description: "JSON file with fields to update", required: true }
15860
16709
  ],
15861
16710
  run: async (c3, a2) => {
15862
- const existing = unwrapData3(await c3.getDownloadClient(a2.id));
15863
- return c3.updateDownloadClient(a2.id, { ...existing, ...readJsonInput3(a2.file) });
16711
+ const existing = unwrapData5(await c3.getDownloadClient(a2.id));
16712
+ return c3.updateDownloadClient(a2.id, { ...existing, ...readJsonInput5(a2.file) });
15864
16713
  }
15865
16714
  },
15866
16715
  {
@@ -17006,6 +17855,12 @@ function getBazarrApiBaseUrl(baseUrl) {
17006
17855
  const normalizedBaseUrl = baseUrl.replace(/\/$/, "");
17007
17856
  return normalizedBaseUrl.endsWith("/api") ? normalizedBaseUrl : `${normalizedBaseUrl}/api`;
17008
17857
  }
17858
+ function getBazarrHeaders(config) {
17859
+ return {
17860
+ "Content-Type": "application/json",
17861
+ ...config.config.headers ?? {}
17862
+ };
17863
+ }
17009
17864
 
17010
17865
  class BazarrClient {
17011
17866
  clientConfig;
@@ -17013,7 +17868,8 @@ class BazarrClient {
17013
17868
  this.clientConfig = createServarrClient(config);
17014
17869
  client6.setConfig({
17015
17870
  baseUrl: getBazarrApiBaseUrl(this.clientConfig.getBaseUrl()),
17016
- headers: this.clientConfig.getHeaders()
17871
+ headers: getBazarrHeaders(this.clientConfig),
17872
+ auth: this.clientConfig.config.apiKey
17017
17873
  });
17018
17874
  }
17019
17875
  async getSystemStatus() {
@@ -17353,7 +18209,8 @@ class BazarrClient {
17353
18209
  this.clientConfig = createServarrClient(updatedConfig);
17354
18210
  client6.setConfig({
17355
18211
  baseUrl: getBazarrApiBaseUrl(this.clientConfig.getBaseUrl()),
17356
- headers: this.clientConfig.getHeaders()
18212
+ headers: getBazarrHeaders(this.clientConfig),
18213
+ auth: this.clientConfig.config.apiKey
17357
18214
  });
17358
18215
  return this.clientConfig.config;
17359
18216
  }
@@ -17995,8 +18852,8 @@ var init_completions = __esm(() => {
17995
18852
 
17996
18853
  // src/cli/index.ts
17997
18854
  init_dist();
17998
- import { readFileSync as readFileSync5 } from "node:fs";
17999
- var { version } = JSON.parse(readFileSync5(new URL("../../package.json", import.meta.url), "utf-8"));
18855
+ import { readFileSync as readFileSync7 } from "node:fs";
18856
+ var { version } = JSON.parse(readFileSync7(new URL("../../package.json", import.meta.url), "utf-8"));
18000
18857
  var main = defineCommand({
18001
18858
  meta: {
18002
18859
  name: "tsarr",