tsarr 2.4.0 → 2.4.2

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
@@ -5259,6 +5259,7 @@ var exports_radarr3 = {};
5259
5259
  __export(exports_radarr3, {
5260
5260
  radarr: () => radarr
5261
5261
  });
5262
+ import { readFileSync as readFileSync2 } from "node:fs";
5262
5263
  function unwrapData(result) {
5263
5264
  return result?.data ?? result;
5264
5265
  }
@@ -5299,6 +5300,10 @@ async function findMovieByTmdbId(client2, tmdbId) {
5299
5300
  function getApiStatus(result) {
5300
5301
  return result?.error?.status ?? result?.response?.status;
5301
5302
  }
5303
+ function readJsonInput(filePath) {
5304
+ const raw = filePath === "-" ? readFileSync2(0, "utf-8") : readFileSync2(filePath, "utf-8");
5305
+ return JSON.parse(raw);
5306
+ }
5302
5307
  var resources, radarr;
5303
5308
  var init_radarr3 = __esm(() => {
5304
5309
  init_radarr2();
@@ -5319,6 +5324,7 @@ var init_radarr3 = __esm(() => {
5319
5324
  name: "get",
5320
5325
  description: "Get a movie by ID",
5321
5326
  args: [{ name: "id", description: "Movie ID", required: true, type: "number" }],
5327
+ columns: ["id", "title", "year", "monitored", "hasFile", "status", "qualityProfileId"],
5322
5328
  run: (c3, a2) => c3.getMovie(a2.id)
5323
5329
  },
5324
5330
  {
@@ -5657,6 +5663,24 @@ var init_radarr3 = __esm(() => {
5657
5663
  args: [{ name: "id", description: "Notification ID", required: true, type: "number" }],
5658
5664
  run: (c3, a2) => c3.getNotification(a2.id)
5659
5665
  },
5666
+ {
5667
+ name: "add",
5668
+ description: "Add a notification from JSON file or stdin",
5669
+ args: [{ name: "file", description: "JSON file path (use - for stdin)", required: true }],
5670
+ run: async (c3, a2) => c3.addNotification(readJsonInput(a2.file))
5671
+ },
5672
+ {
5673
+ name: "edit",
5674
+ description: "Edit a notification (merges JSON with existing)",
5675
+ args: [
5676
+ { name: "id", description: "Notification ID", required: true, type: "number" },
5677
+ { name: "file", description: "JSON file with fields to update", required: true }
5678
+ ],
5679
+ run: async (c3, a2) => {
5680
+ const existing = unwrapData(await c3.getNotification(a2.id));
5681
+ return c3.updateNotification(a2.id, { ...existing, ...readJsonInput(a2.file) });
5682
+ }
5683
+ },
5660
5684
  {
5661
5685
  name: "delete",
5662
5686
  description: "Delete a notification",
@@ -5687,6 +5711,24 @@ var init_radarr3 = __esm(() => {
5687
5711
  args: [{ name: "id", description: "Download client ID", required: true, type: "number" }],
5688
5712
  run: (c3, a2) => c3.getDownloadClient(a2.id)
5689
5713
  },
5714
+ {
5715
+ name: "add",
5716
+ description: "Add a download client from JSON file or stdin",
5717
+ args: [{ name: "file", description: "JSON file path (use - for stdin)", required: true }],
5718
+ run: async (c3, a2) => c3.addDownloadClient(readJsonInput(a2.file))
5719
+ },
5720
+ {
5721
+ name: "edit",
5722
+ description: "Edit a download client (merges JSON with existing)",
5723
+ args: [
5724
+ { name: "id", description: "Download client ID", required: true, type: "number" },
5725
+ { name: "file", description: "JSON file with fields to update", required: true }
5726
+ ],
5727
+ run: async (c3, a2) => {
5728
+ const existing = unwrapData(await c3.getDownloadClient(a2.id));
5729
+ return c3.updateDownloadClient(a2.id, { ...existing, ...readJsonInput(a2.file) });
5730
+ }
5731
+ },
5690
5732
  {
5691
5733
  name: "delete",
5692
5734
  description: "Delete a download client",
@@ -5754,6 +5796,24 @@ var init_radarr3 = __esm(() => {
5754
5796
  args: [{ name: "id", description: "Import list ID", required: true, type: "number" }],
5755
5797
  run: (c3, a2) => c3.getImportList(a2.id)
5756
5798
  },
5799
+ {
5800
+ name: "add",
5801
+ description: "Add an import list from JSON file or stdin",
5802
+ args: [{ name: "file", description: "JSON file path (use - for stdin)", required: true }],
5803
+ run: async (c3, a2) => c3.addImportList(readJsonInput(a2.file))
5804
+ },
5805
+ {
5806
+ name: "edit",
5807
+ description: "Edit an import list (merges JSON with existing)",
5808
+ args: [
5809
+ { name: "id", description: "Import list ID", required: true, type: "number" },
5810
+ { name: "file", description: "JSON file with fields to update", required: true }
5811
+ ],
5812
+ run: async (c3, a2) => {
5813
+ const existing = unwrapData(await c3.getImportList(a2.id));
5814
+ return c3.updateImportList(a2.id, { ...existing, ...readJsonInput(a2.file) });
5815
+ }
5816
+ },
5757
5817
  {
5758
5818
  name: "delete",
5759
5819
  description: "Delete an import list",
@@ -8029,6 +8089,7 @@ class SonarrClient {
8029
8089
  query.end = endDate;
8030
8090
  if (unmonitored !== undefined)
8031
8091
  query.unmonitored = unmonitored;
8092
+ query.includeSeries = true;
8032
8093
  return getApiV3Calendar2(Object.keys(query).length > 0 ? { query } : {});
8033
8094
  }
8034
8095
  async getCalendarFeed(pastDays, futureDays, tags) {
@@ -8177,6 +8238,7 @@ var exports_sonarr3 = {};
8177
8238
  __export(exports_sonarr3, {
8178
8239
  sonarr: () => sonarr
8179
8240
  });
8241
+ import { readFileSync as readFileSync3 } from "node:fs";
8180
8242
  function unwrapData2(result) {
8181
8243
  return result?.data ?? result;
8182
8244
  }
@@ -8236,6 +8298,10 @@ async function findSeriesByTvdbId(client3, tvdbId) {
8236
8298
  function getApiStatus2(result) {
8237
8299
  return result?.error?.status ?? result?.response?.status;
8238
8300
  }
8301
+ function readJsonInput2(filePath) {
8302
+ const raw = filePath === "-" ? readFileSync3(0, "utf-8") : readFileSync3(filePath, "utf-8");
8303
+ return JSON.parse(raw);
8304
+ }
8239
8305
  var resources2, sonarr;
8240
8306
  var init_sonarr3 = __esm(() => {
8241
8307
  init_sonarr2();
@@ -8268,7 +8334,21 @@ var init_sonarr3 = __esm(() => {
8268
8334
  name: "get",
8269
8335
  description: "Get a series by ID",
8270
8336
  args: [{ name: "id", description: "Series ID", required: true, type: "number" }],
8271
- run: (c3, a2) => c3.getSeriesById(a2.id)
8337
+ columns: [
8338
+ "id",
8339
+ "title",
8340
+ "year",
8341
+ "monitored",
8342
+ "seasonCount",
8343
+ "episodeCount",
8344
+ "network",
8345
+ "status"
8346
+ ],
8347
+ run: async (c3, a2) => {
8348
+ const result = await c3.getSeriesById(a2.id);
8349
+ const series = unwrapData2(result);
8350
+ return formatSeriesListItem(series);
8351
+ }
8272
8352
  },
8273
8353
  {
8274
8354
  name: "search",
@@ -8438,6 +8518,12 @@ var init_sonarr3 = __esm(() => {
8438
8518
  description: "List quality profiles",
8439
8519
  columns: ["id", "name"],
8440
8520
  run: (c3) => c3.getQualityProfiles()
8521
+ },
8522
+ {
8523
+ name: "get",
8524
+ description: "Get a quality profile by ID",
8525
+ args: [{ name: "id", description: "Profile ID", required: true, type: "number" }],
8526
+ run: (c3, a2) => c3.getQualityProfile(a2.id)
8441
8527
  }
8442
8528
  ]
8443
8529
  },
@@ -8586,7 +8672,14 @@ var init_sonarr3 = __esm(() => {
8586
8672
  { name: "unmonitored", description: "Include unmonitored", type: "boolean" }
8587
8673
  ],
8588
8674
  columns: ["id", "seriesTitle", "title", "seasonNumber", "episodeNumber", "airDateUtc"],
8589
- run: (c3, a2) => c3.getCalendar(a2.start, a2.end, a2.unmonitored)
8675
+ run: async (c3, a2) => {
8676
+ const result = await c3.getCalendar(a2.start, a2.end, a2.unmonitored);
8677
+ const episodes = unwrapData2(result);
8678
+ return episodes.map((ep) => ({
8679
+ ...ep,
8680
+ seriesTitle: ep.seriesTitle || ep.series?.title || "—"
8681
+ }));
8682
+ }
8590
8683
  }
8591
8684
  ]
8592
8685
  },
@@ -8606,6 +8699,24 @@ var init_sonarr3 = __esm(() => {
8606
8699
  args: [{ name: "id", description: "Notification ID", required: true, type: "number" }],
8607
8700
  run: (c3, a2) => c3.getNotification(a2.id)
8608
8701
  },
8702
+ {
8703
+ name: "add",
8704
+ description: "Add a notification from JSON file or stdin",
8705
+ args: [{ name: "file", description: "JSON file path (use - for stdin)", required: true }],
8706
+ run: async (c3, a2) => c3.addNotification(readJsonInput2(a2.file))
8707
+ },
8708
+ {
8709
+ name: "edit",
8710
+ description: "Edit a notification (merges JSON with existing)",
8711
+ args: [
8712
+ { name: "id", description: "Notification ID", required: true, type: "number" },
8713
+ { name: "file", description: "JSON file with fields to update", required: true }
8714
+ ],
8715
+ run: async (c3, a2) => {
8716
+ const existing = unwrapData2(await c3.getNotification(a2.id));
8717
+ return c3.updateNotification(a2.id, { ...existing, ...readJsonInput2(a2.file) });
8718
+ }
8719
+ },
8609
8720
  {
8610
8721
  name: "delete",
8611
8722
  description: "Delete a notification",
@@ -8636,6 +8747,24 @@ var init_sonarr3 = __esm(() => {
8636
8747
  args: [{ name: "id", description: "Download client ID", required: true, type: "number" }],
8637
8748
  run: (c3, a2) => c3.getDownloadClient(a2.id)
8638
8749
  },
8750
+ {
8751
+ name: "add",
8752
+ description: "Add a download client from JSON file or stdin",
8753
+ args: [{ name: "file", description: "JSON file path (use - for stdin)", required: true }],
8754
+ run: async (c3, a2) => c3.addDownloadClient(readJsonInput2(a2.file))
8755
+ },
8756
+ {
8757
+ name: "edit",
8758
+ description: "Edit a download client (merges JSON with existing)",
8759
+ args: [
8760
+ { name: "id", description: "Download client ID", required: true, type: "number" },
8761
+ { name: "file", description: "JSON file with fields to update", required: true }
8762
+ ],
8763
+ run: async (c3, a2) => {
8764
+ const existing = unwrapData2(await c3.getDownloadClient(a2.id));
8765
+ return c3.updateDownloadClient(a2.id, { ...existing, ...readJsonInput2(a2.file) });
8766
+ }
8767
+ },
8639
8768
  {
8640
8769
  name: "delete",
8641
8770
  description: "Delete a download client",
@@ -8703,6 +8832,24 @@ var init_sonarr3 = __esm(() => {
8703
8832
  args: [{ name: "id", description: "Import list ID", required: true, type: "number" }],
8704
8833
  run: (c3, a2) => c3.getImportList(a2.id)
8705
8834
  },
8835
+ {
8836
+ name: "add",
8837
+ description: "Add an import list from JSON file or stdin",
8838
+ args: [{ name: "file", description: "JSON file path (use - for stdin)", required: true }],
8839
+ run: async (c3, a2) => c3.addImportList(readJsonInput2(a2.file))
8840
+ },
8841
+ {
8842
+ name: "edit",
8843
+ description: "Edit an import list (merges JSON with existing)",
8844
+ args: [
8845
+ { name: "id", description: "Import list ID", required: true, type: "number" },
8846
+ { name: "file", description: "JSON file with fields to update", required: true }
8847
+ ],
8848
+ run: async (c3, a2) => {
8849
+ const existing = unwrapData2(await c3.getImportList(a2.id));
8850
+ return c3.updateImportList(a2.id, { ...existing, ...readJsonInput2(a2.file) });
8851
+ }
8852
+ },
8706
8853
  {
8707
8854
  name: "delete",
8708
8855
  description: "Delete an import list",
@@ -9822,6 +9969,14 @@ var getApiV1Album = (options) => (options?.client ?? client3).get({
9822
9969
  }],
9823
9970
  url: "/api/v1/customformat/schema",
9824
9971
  ...options
9972
+ }), getApiV1WantedCutoff = (options) => (options?.client ?? client3).get({
9973
+ security: [{ name: "X-Api-Key", type: "apiKey" }, {
9974
+ in: "query",
9975
+ name: "apikey",
9976
+ type: "apiKey"
9977
+ }],
9978
+ url: "/api/v1/wanted/cutoff",
9979
+ ...options
9825
9980
  }), getApiV1Diskspace = (options) => (options?.client ?? client3).get({
9826
9981
  security: [{ name: "X-Api-Key", type: "apiKey" }, {
9827
9982
  in: "query",
@@ -10230,6 +10385,14 @@ var getApiV1Album = (options) => (options?.client ?? client3).get({
10230
10385
  }],
10231
10386
  url: "/api/v1/config/metadataprovider",
10232
10387
  ...options
10388
+ }), getApiV1WantedMissing = (options) => (options?.client ?? client3).get({
10389
+ security: [{ name: "X-Api-Key", type: "apiKey" }, {
10390
+ in: "query",
10391
+ name: "apikey",
10392
+ type: "apiKey"
10393
+ }],
10394
+ url: "/api/v1/wanted/missing",
10395
+ ...options
10233
10396
  }), getApiV1ConfigNamingById = (options) => (options.client ?? client3).get({
10234
10397
  security: [{ name: "X-Api-Key", type: "apiKey" }, {
10235
10398
  in: "query",
@@ -10698,6 +10861,7 @@ class LidarrClient {
10698
10861
  query.end = end;
10699
10862
  if (unmonitored !== undefined)
10700
10863
  query.unmonitored = unmonitored;
10864
+ query.includeArtist = true;
10701
10865
  return getApiV1Calendar(Object.keys(query).length > 0 ? { query } : {});
10702
10866
  }
10703
10867
  async getCalendarFeed(pastDays, futureDays, tags) {
@@ -11055,6 +11219,34 @@ class LidarrClient {
11055
11219
  async removeBlocklistItemsBulk(ids) {
11056
11220
  return deleteApiV1BlocklistBulk({ body: { ids } });
11057
11221
  }
11222
+ async getWantedMissing(page, pageSize, sortKey, sortDirection, monitored) {
11223
+ const query = { includeArtist: true };
11224
+ if (page !== undefined)
11225
+ query.page = page;
11226
+ if (pageSize !== undefined)
11227
+ query.pageSize = pageSize;
11228
+ if (sortKey)
11229
+ query.sortKey = sortKey;
11230
+ if (sortDirection)
11231
+ query.sortDirection = sortDirection;
11232
+ if (monitored !== undefined)
11233
+ query.monitored = monitored;
11234
+ return getApiV1WantedMissing(Object.keys(query).length > 0 ? { query } : {});
11235
+ }
11236
+ async getWantedCutoff(page, pageSize, sortKey, sortDirection, monitored) {
11237
+ const query = { includeArtist: true };
11238
+ if (page !== undefined)
11239
+ query.page = page;
11240
+ if (pageSize !== undefined)
11241
+ query.pageSize = pageSize;
11242
+ if (sortKey)
11243
+ query.sortKey = sortKey;
11244
+ if (sortDirection)
11245
+ query.sortDirection = sortDirection;
11246
+ if (monitored !== undefined)
11247
+ query.monitored = monitored;
11248
+ return getApiV1WantedCutoff(Object.keys(query).length > 0 ? { query } : {});
11249
+ }
11058
11250
  updateConfig(newConfig) {
11059
11251
  const updatedConfig = { ...this.clientConfig.config, ...newConfig };
11060
11252
  this.clientConfig = createServarrClient(updatedConfig);
@@ -11076,6 +11268,38 @@ var exports_lidarr3 = {};
11076
11268
  __export(exports_lidarr3, {
11077
11269
  lidarr: () => lidarr
11078
11270
  });
11271
+ import { readFileSync as readFileSync4 } from "node:fs";
11272
+ function unwrapData3(result) {
11273
+ return result?.data ?? result;
11274
+ }
11275
+ function formatAlbumListItem(album) {
11276
+ return {
11277
+ ...album,
11278
+ artistName: album?.artistName ?? album?.artist?.artistName ?? "—"
11279
+ };
11280
+ }
11281
+ function formatQueueListItem(item) {
11282
+ return {
11283
+ ...item,
11284
+ artistName: item?.artistName ?? item?.artist?.artistName ?? "—"
11285
+ };
11286
+ }
11287
+ function formatHistoryListItem(item) {
11288
+ return {
11289
+ ...item,
11290
+ artistName: item?.artistName ?? item?.artist?.artistName ?? "—"
11291
+ };
11292
+ }
11293
+ function formatBlocklistItem(item) {
11294
+ return {
11295
+ ...item,
11296
+ artistName: item?.artistName ?? item?.artist?.artistName ?? "—"
11297
+ };
11298
+ }
11299
+ function readJsonInput3(filePath) {
11300
+ const raw = filePath === "-" ? readFileSync4(0, "utf-8") : readFileSync4(filePath, "utf-8");
11301
+ return JSON.parse(raw);
11302
+ }
11079
11303
  var resources3, lidarr;
11080
11304
  var init_lidarr3 = __esm(() => {
11081
11305
  init_lidarr2();
@@ -11111,31 +11335,28 @@ var init_lidarr3 = __esm(() => {
11111
11335
  description: "Search and add an artist",
11112
11336
  args: [{ name: "term", description: "Search term", required: true }],
11113
11337
  run: async (c3, a2) => {
11114
- const searchResult = await c3.searchArtists(a2.term);
11115
- const results = searchResult?.data ?? searchResult;
11338
+ const results = unwrapData3(await c3.searchArtists(a2.term));
11116
11339
  if (!Array.isArray(results) || results.length === 0) {
11117
11340
  throw new Error("No artists found.");
11118
11341
  }
11119
- const artistId = await promptSelect("Select an artist:", results.map((ar) => ({
11120
- label: ar.artistName,
11121
- value: String(ar.foreignArtistId)
11342
+ const artistId = await promptSelect("Select an artist:", results.map((artist2) => ({
11343
+ label: artist2.artistName,
11344
+ value: String(artist2.foreignArtistId)
11122
11345
  })));
11123
- const artist = results.find((ar) => String(ar.foreignArtistId) === artistId);
11346
+ const artist = results.find((item) => String(item.foreignArtistId) === artistId);
11124
11347
  if (!artist) {
11125
11348
  throw new Error("Selected artist was not found in the search results.");
11126
11349
  }
11127
- const profilesResult = await c3.getQualityProfiles();
11128
- const profiles = profilesResult?.data ?? profilesResult;
11350
+ const profiles = unwrapData3(await c3.getQualityProfiles());
11129
11351
  if (!Array.isArray(profiles) || profiles.length === 0) {
11130
11352
  throw new Error("No quality profiles found. Configure one in Lidarr first.");
11131
11353
  }
11132
- const profileId = await promptSelect("Select quality profile:", profiles.map((p) => ({ label: p.name, value: String(p.id) })));
11133
- const foldersResult = await c3.getRootFolders();
11134
- const folders = foldersResult?.data ?? foldersResult;
11354
+ const profileId = await promptSelect("Select quality profile:", profiles.map((profile) => ({ label: profile.name, value: String(profile.id) })));
11355
+ const folders = unwrapData3(await c3.getRootFolders());
11135
11356
  if (!Array.isArray(folders) || folders.length === 0) {
11136
11357
  throw new Error("No root folders found. Configure one in Lidarr first.");
11137
11358
  }
11138
- const rootFolderPath = await promptSelect("Select root folder:", folders.map((f3) => ({ label: f3.path, value: f3.path })));
11359
+ const rootFolderPath = await promptSelect("Select root folder:", folders.map((folder) => ({ label: folder.path, value: folder.path })));
11139
11360
  const confirmed = await promptConfirm(`Add "${artist.artistName}"?`, !!a2.yes);
11140
11361
  if (!confirmed)
11141
11362
  throw new Error("Cancelled.");
@@ -11158,15 +11379,16 @@ var init_lidarr3 = __esm(() => {
11158
11379
  { name: "tags", description: "Comma-separated tag IDs" }
11159
11380
  ],
11160
11381
  run: async (c3, a2) => {
11161
- const result = await c3.getArtist(a2.id);
11162
- const artist = result?.data ?? result;
11382
+ const artist = unwrapData3(await c3.getArtist(a2.id));
11163
11383
  const updates = { ...artist };
11164
11384
  if (a2.monitored !== undefined)
11165
11385
  updates.monitored = a2.monitored === "true";
11166
- if (a2["quality-profile-id"] !== undefined)
11386
+ if (a2["quality-profile-id"] !== undefined) {
11167
11387
  updates.qualityProfileId = Number(a2["quality-profile-id"]);
11168
- if (a2.tags !== undefined)
11169
- updates.tags = a2.tags.split(",").map((t2) => Number(t2.trim()));
11388
+ }
11389
+ if (a2.tags !== undefined) {
11390
+ updates.tags = a2.tags.split(",").map((tag) => Number(tag.trim()));
11391
+ }
11170
11392
  return c3.updateArtist(a2.id, updates);
11171
11393
  }
11172
11394
  },
@@ -11198,22 +11420,56 @@ var init_lidarr3 = __esm(() => {
11198
11420
  {
11199
11421
  name: "list",
11200
11422
  description: "List all albums",
11201
- columns: ["id", "title", "artistId", "monitored"],
11202
- run: (c3) => c3.getAlbums()
11423
+ columns: ["id", "artistName", "title", "monitored"],
11424
+ run: async (c3) => {
11425
+ const albums = unwrapData3(await c3.getAlbums());
11426
+ return albums.map(formatAlbumListItem);
11427
+ }
11203
11428
  },
11204
11429
  {
11205
11430
  name: "get",
11206
11431
  description: "Get an album by ID",
11207
11432
  args: [{ name: "id", description: "Album ID", required: true, type: "number" }],
11208
- run: (c3, a2) => c3.getAlbum(a2.id)
11433
+ run: async (c3, a2) => {
11434
+ const album = unwrapData3(await c3.getAlbum(a2.id));
11435
+ return formatAlbumListItem(album);
11436
+ }
11209
11437
  },
11210
11438
  {
11211
11439
  name: "search",
11212
11440
  description: "Search for albums",
11213
11441
  args: [{ name: "term", description: "Search term", required: true }],
11214
- columns: ["foreignAlbumId", "title", "artistId"],
11442
+ columns: ["foreignAlbumId", "artistName", "title", "monitored"],
11215
11443
  idField: "foreignAlbumId",
11216
- run: (c3, a2) => c3.searchAlbums(a2.term)
11444
+ run: async (c3, a2) => {
11445
+ const albums = unwrapData3(await c3.searchAlbums(a2.term));
11446
+ return albums.map(formatAlbumListItem);
11447
+ }
11448
+ },
11449
+ {
11450
+ name: "add",
11451
+ description: "Add an album from JSON file or stdin",
11452
+ args: [{ name: "file", description: "JSON file path (use - for stdin)", required: true }],
11453
+ run: async (c3, a2) => c3.addAlbum(readJsonInput3(a2.file))
11454
+ },
11455
+ {
11456
+ name: "edit",
11457
+ description: "Edit an album (merges JSON with existing)",
11458
+ args: [
11459
+ { name: "id", description: "Album ID", required: true, type: "number" },
11460
+ { name: "file", description: "JSON file with fields to update", required: true }
11461
+ ],
11462
+ run: async (c3, a2) => {
11463
+ const existing = unwrapData3(await c3.getAlbum(a2.id));
11464
+ return c3.updateAlbum(a2.id, { ...existing, ...readJsonInput3(a2.file) });
11465
+ }
11466
+ },
11467
+ {
11468
+ name: "delete",
11469
+ description: "Delete an album",
11470
+ args: [{ name: "id", description: "Album ID", required: true, type: "number" }],
11471
+ confirmMessage: "Are you sure you want to delete this album?",
11472
+ run: (c3, a2) => c3.deleteAlbum(a2.id)
11217
11473
  }
11218
11474
  ]
11219
11475
  },
@@ -11226,6 +11482,12 @@ var init_lidarr3 = __esm(() => {
11226
11482
  description: "List quality profiles",
11227
11483
  columns: ["id", "name"],
11228
11484
  run: (c3) => c3.getQualityProfiles()
11485
+ },
11486
+ {
11487
+ name: "get",
11488
+ description: "Get a quality profile by ID",
11489
+ args: [{ name: "id", description: "Profile ID", required: true, type: "number" }],
11490
+ run: (c3, a2) => c3.getQualityProfile(a2.id)
11229
11491
  }
11230
11492
  ]
11231
11493
  },
@@ -11248,7 +11510,7 @@ var init_lidarr3 = __esm(() => {
11248
11510
  const tagResult = await c3.getTag(a2.id);
11249
11511
  if (tagResult?.error)
11250
11512
  return tagResult;
11251
- const tag = tagResult?.data ?? tagResult;
11513
+ const tag = unwrapData3(tagResult);
11252
11514
  const deleteResult = await c3.deleteTag(a2.id);
11253
11515
  if (deleteResult?.error)
11254
11516
  return deleteResult;
@@ -11264,64 +11526,312 @@ var init_lidarr3 = __esm(() => {
11264
11526
  ]
11265
11527
  },
11266
11528
  {
11267
- name: "rootfolder",
11268
- description: "Manage root folders",
11529
+ name: "queue",
11530
+ description: "Manage download queue",
11269
11531
  actions: [
11270
11532
  {
11271
11533
  name: "list",
11272
- description: "List root folders",
11273
- columns: ["id", "path", "freeSpace"],
11274
- run: (c3) => c3.getRootFolders()
11534
+ description: "List queue items",
11535
+ columns: ["id", "artistName", "title", "status", "sizeleft", "timeleft"],
11536
+ run: async (c3) => {
11537
+ const items = unwrapData3(await c3.getQueue());
11538
+ return items.map(formatQueueListItem);
11539
+ }
11275
11540
  },
11276
11541
  {
11277
- name: "add",
11278
- description: "Add a root folder",
11279
- args: [{ name: "path", description: "Folder path", required: true }],
11280
- run: (c3, a2) => c3.addRootFolder(a2.path)
11542
+ name: "status",
11543
+ description: "Get queue status",
11544
+ run: (c3) => c3.getQueueStatus()
11281
11545
  },
11282
11546
  {
11283
11547
  name: "delete",
11284
- description: "Delete a root folder",
11285
- args: [{ name: "id", description: "Root folder ID", required: true, type: "number" }],
11286
- confirmMessage: "Are you sure you want to delete this root folder?",
11287
- run: (c3, a2) => c3.deleteRootFolder(a2.id)
11548
+ description: "Remove an item from the queue",
11549
+ args: [
11550
+ { name: "id", description: "Queue item ID", required: true, type: "number" },
11551
+ { name: "blocklist", description: "Add to blocklist", type: "boolean" },
11552
+ {
11553
+ name: "remove-from-client",
11554
+ description: "Remove from download client",
11555
+ type: "boolean"
11556
+ }
11557
+ ],
11558
+ confirmMessage: "Are you sure you want to remove this queue item?",
11559
+ run: (c3, a2) => c3.removeQueueItem(a2.id, a2["remove-from-client"], a2.blocklist)
11560
+ },
11561
+ {
11562
+ name: "grab",
11563
+ description: "Force download a queue item",
11564
+ args: [{ name: "id", description: "Queue item ID", required: true, type: "number" }],
11565
+ run: (c3, a2) => c3.grabQueueItem(a2.id)
11288
11566
  }
11289
11567
  ]
11290
11568
  },
11291
11569
  {
11292
- name: "system",
11293
- description: "System information",
11570
+ name: "history",
11571
+ description: "View history",
11294
11572
  actions: [
11295
11573
  {
11296
- name: "status",
11297
- description: "Get system status",
11298
- run: (c3) => c3.getSystemStatus()
11299
- },
11574
+ name: "list",
11575
+ description: "List recent history",
11576
+ args: [
11577
+ { name: "since", description: "Start date (ISO 8601, e.g. 2024-01-01)" },
11578
+ { name: "until", description: "End date (ISO 8601, e.g. 2024-12-31)" }
11579
+ ],
11580
+ columns: ["id", "artistName", "sourceTitle", "eventType", "date"],
11581
+ run: async (c3, a2) => {
11582
+ const items = a2.since ? unwrapData3(await c3.getHistorySince(a2.since)) : unwrapData3(await c3.getHistory());
11583
+ const filtered = a2.until ? items.filter((item) => new Date(item.date) <= new Date(a2.until)) : items;
11584
+ return filtered.map(formatHistoryListItem);
11585
+ }
11586
+ }
11587
+ ]
11588
+ },
11589
+ {
11590
+ name: "calendar",
11591
+ description: "View upcoming releases",
11592
+ actions: [
11300
11593
  {
11301
- name: "health",
11302
- description: "Get health check results",
11303
- columns: ["type", "message", "source"],
11304
- run: (c3) => c3.getHealth()
11594
+ name: "list",
11595
+ description: "List upcoming album releases",
11596
+ args: [
11597
+ { name: "start", description: "Start date (ISO 8601)" },
11598
+ { name: "end", description: "End date (ISO 8601)" },
11599
+ { name: "unmonitored", description: "Include unmonitored", type: "boolean" }
11600
+ ],
11601
+ columns: ["id", "artistName", "title", "releaseDate"],
11602
+ run: async (c3, a2) => {
11603
+ const albums = unwrapData3(await c3.getCalendar(a2.start, a2.end, a2.unmonitored));
11604
+ return albums.map(formatAlbumListItem);
11605
+ }
11305
11606
  }
11306
11607
  ]
11307
- }
11308
- ];
11309
- lidarr = buildServiceCommand("lidarr", "Manage Lidarr (Music)", (config) => new LidarrClient(config), resources3);
11310
- });
11311
-
11312
- // src/generated/readarr/core/bodySerializer.gen.ts
11313
- var jsonBodySerializer4;
11314
- var init_bodySerializer_gen4 = __esm(() => {
11315
- jsonBodySerializer4 = {
11316
- bodySerializer: (body) => JSON.stringify(body, (_key, value) => typeof value === "bigint" ? value.toString() : value)
11317
- };
11318
- });
11319
-
11320
- // src/generated/readarr/core/serverSentEvents.gen.ts
11321
- var createSseClient4 = ({
11322
- onRequest,
11323
- onSseError,
11324
- onSseEvent,
11608
+ },
11609
+ {
11610
+ name: "notification",
11611
+ description: "Manage notifications",
11612
+ actions: [
11613
+ {
11614
+ name: "list",
11615
+ description: "List notification providers",
11616
+ columns: ["id", "name", "implementation"],
11617
+ run: (c3) => c3.getNotifications()
11618
+ },
11619
+ {
11620
+ name: "get",
11621
+ description: "Get a notification by ID",
11622
+ args: [{ name: "id", description: "Notification ID", required: true, type: "number" }],
11623
+ run: (c3, a2) => c3.getNotification(a2.id)
11624
+ },
11625
+ {
11626
+ name: "add",
11627
+ description: "Add a notification from JSON file or stdin",
11628
+ args: [{ name: "file", description: "JSON file path (use - for stdin)", required: true }],
11629
+ run: async (c3, a2) => c3.addNotification(readJsonInput3(a2.file))
11630
+ },
11631
+ {
11632
+ name: "edit",
11633
+ description: "Edit a notification (merges JSON with existing)",
11634
+ args: [
11635
+ { name: "id", description: "Notification ID", required: true, type: "number" },
11636
+ { name: "file", description: "JSON file with fields to update", required: true }
11637
+ ],
11638
+ run: async (c3, a2) => {
11639
+ const existing = unwrapData3(await c3.getNotification(a2.id));
11640
+ return c3.updateNotification(a2.id, { ...existing, ...readJsonInput3(a2.file) });
11641
+ }
11642
+ },
11643
+ {
11644
+ name: "delete",
11645
+ description: "Delete a notification",
11646
+ args: [{ name: "id", description: "Notification ID", required: true, type: "number" }],
11647
+ confirmMessage: "Are you sure you want to delete this notification?",
11648
+ run: (c3, a2) => c3.deleteNotification(a2.id)
11649
+ },
11650
+ {
11651
+ name: "test",
11652
+ description: "Test all notifications",
11653
+ run: (c3) => c3.testAllNotifications()
11654
+ }
11655
+ ]
11656
+ },
11657
+ {
11658
+ name: "downloadclient",
11659
+ description: "Manage download clients",
11660
+ actions: [
11661
+ {
11662
+ name: "list",
11663
+ description: "List download clients",
11664
+ columns: ["id", "name", "implementation", "enable"],
11665
+ run: (c3) => c3.getDownloadClients()
11666
+ },
11667
+ {
11668
+ name: "get",
11669
+ description: "Get a download client by ID",
11670
+ args: [{ name: "id", description: "Download client ID", required: true, type: "number" }],
11671
+ run: (c3, a2) => c3.getDownloadClient(a2.id)
11672
+ },
11673
+ {
11674
+ name: "add",
11675
+ description: "Add a download client from JSON file or stdin",
11676
+ args: [{ name: "file", description: "JSON file path (use - for stdin)", required: true }],
11677
+ run: async (c3, a2) => c3.addDownloadClient(readJsonInput3(a2.file))
11678
+ },
11679
+ {
11680
+ name: "edit",
11681
+ description: "Edit a download client (merges JSON with existing)",
11682
+ args: [
11683
+ { name: "id", description: "Download client ID", required: true, type: "number" },
11684
+ { name: "file", description: "JSON file with fields to update", required: true }
11685
+ ],
11686
+ run: async (c3, a2) => {
11687
+ const existing = unwrapData3(await c3.getDownloadClient(a2.id));
11688
+ return c3.updateDownloadClient(a2.id, { ...existing, ...readJsonInput3(a2.file) });
11689
+ }
11690
+ },
11691
+ {
11692
+ name: "delete",
11693
+ description: "Delete a download client",
11694
+ args: [{ name: "id", description: "Download client ID", required: true, type: "number" }],
11695
+ confirmMessage: "Are you sure you want to delete this download client?",
11696
+ run: (c3, a2) => c3.deleteDownloadClient(a2.id)
11697
+ },
11698
+ {
11699
+ name: "test",
11700
+ description: "Test all download clients",
11701
+ run: (c3) => c3.testAllDownloadClients()
11702
+ }
11703
+ ]
11704
+ },
11705
+ {
11706
+ name: "blocklist",
11707
+ description: "View blocked releases",
11708
+ actions: [
11709
+ {
11710
+ name: "list",
11711
+ description: "List blocked releases",
11712
+ columns: ["id", "artistName", "sourceTitle", "date"],
11713
+ run: async (c3) => {
11714
+ const items = unwrapData3(await c3.getBlocklist());
11715
+ return items.map(formatBlocklistItem);
11716
+ }
11717
+ },
11718
+ {
11719
+ name: "delete",
11720
+ description: "Remove a release from the blocklist",
11721
+ args: [{ name: "id", description: "Blocklist item ID", required: true, type: "number" }],
11722
+ confirmMessage: "Are you sure you want to remove this blocklist entry?",
11723
+ run: (c3, a2) => c3.removeBlocklistItem(a2.id)
11724
+ }
11725
+ ]
11726
+ },
11727
+ {
11728
+ name: "wanted",
11729
+ description: "View missing and cutoff unmet albums",
11730
+ actions: [
11731
+ {
11732
+ name: "missing",
11733
+ description: "List albums with missing tracks",
11734
+ columns: ["id", "artistName", "title", "releaseDate"],
11735
+ run: async (c3) => {
11736
+ const albums = unwrapData3(await c3.getWantedMissing());
11737
+ return albums.map(formatAlbumListItem);
11738
+ }
11739
+ },
11740
+ {
11741
+ name: "cutoff",
11742
+ description: "List albums below quality cutoff",
11743
+ columns: ["id", "artistName", "title", "releaseDate"],
11744
+ run: async (c3) => {
11745
+ const albums = unwrapData3(await c3.getWantedCutoff());
11746
+ return albums.map(formatAlbumListItem);
11747
+ }
11748
+ }
11749
+ ]
11750
+ },
11751
+ {
11752
+ name: "importlist",
11753
+ description: "Manage import lists",
11754
+ actions: [
11755
+ {
11756
+ name: "list",
11757
+ description: "List import lists",
11758
+ columns: ["id", "name", "implementation", "enable"],
11759
+ run: (c3) => c3.getImportLists()
11760
+ },
11761
+ {
11762
+ name: "get",
11763
+ description: "Get an import list by ID",
11764
+ args: [{ name: "id", description: "Import list ID", required: true, type: "number" }],
11765
+ run: (c3, a2) => c3.getImportList(a2.id)
11766
+ },
11767
+ {
11768
+ name: "delete",
11769
+ description: "Delete an import list",
11770
+ args: [{ name: "id", description: "Import list ID", required: true, type: "number" }],
11771
+ confirmMessage: "Are you sure you want to delete this import list?",
11772
+ run: (c3, a2) => c3.deleteImportList(a2.id)
11773
+ }
11774
+ ]
11775
+ },
11776
+ {
11777
+ name: "rootfolder",
11778
+ description: "Manage root folders",
11779
+ actions: [
11780
+ {
11781
+ name: "list",
11782
+ description: "List root folders",
11783
+ columns: ["id", "path", "freeSpace"],
11784
+ run: (c3) => c3.getRootFolders()
11785
+ },
11786
+ {
11787
+ name: "add",
11788
+ description: "Add a root folder",
11789
+ args: [{ name: "path", description: "Folder path", required: true }],
11790
+ run: (c3, a2) => c3.addRootFolder(a2.path)
11791
+ },
11792
+ {
11793
+ name: "delete",
11794
+ description: "Delete a root folder",
11795
+ args: [{ name: "id", description: "Root folder ID", required: true, type: "number" }],
11796
+ confirmMessage: "Are you sure you want to delete this root folder?",
11797
+ run: (c3, a2) => c3.deleteRootFolder(a2.id)
11798
+ }
11799
+ ]
11800
+ },
11801
+ {
11802
+ name: "system",
11803
+ description: "System information",
11804
+ actions: [
11805
+ {
11806
+ name: "status",
11807
+ description: "Get system status",
11808
+ run: (c3) => c3.getSystemStatus()
11809
+ },
11810
+ {
11811
+ name: "health",
11812
+ description: "Get health check results",
11813
+ columns: ["type", "message", "source"],
11814
+ run: (c3) => c3.getHealth()
11815
+ }
11816
+ ]
11817
+ }
11818
+ ];
11819
+ lidarr = buildServiceCommand("lidarr", "Manage Lidarr (Music)", (config) => new LidarrClient(config), resources3);
11820
+ });
11821
+
11822
+ // src/generated/readarr/core/bodySerializer.gen.ts
11823
+ var jsonBodySerializer4;
11824
+ var init_bodySerializer_gen4 = __esm(() => {
11825
+ jsonBodySerializer4 = {
11826
+ bodySerializer: (body) => JSON.stringify(body, (_key, value) => typeof value === "bigint" ? value.toString() : value)
11827
+ };
11828
+ });
11829
+
11830
+ // src/generated/readarr/core/serverSentEvents.gen.ts
11831
+ var createSseClient4 = ({
11832
+ onRequest,
11833
+ onSseError,
11834
+ onSseEvent,
11325
11835
  responseTransformer,
11326
11836
  responseValidator,
11327
11837
  sseDefaultRetryDelay,
@@ -12282,6 +12792,22 @@ var getApiV1Author = (options) => (options?.client ?? client4).get({
12282
12792
  }],
12283
12793
  url: "/api/v1/book/lookup",
12284
12794
  ...options
12795
+ }), getApiV1Calendar2 = (options) => (options?.client ?? client4).get({
12796
+ security: [{ name: "X-Api-Key", type: "apiKey" }, {
12797
+ in: "query",
12798
+ name: "apikey",
12799
+ type: "apiKey"
12800
+ }],
12801
+ url: "/api/v1/calendar",
12802
+ ...options
12803
+ }), getFeedV1CalendarReadarrIcs = (options) => (options?.client ?? client4).get({
12804
+ security: [{ name: "X-Api-Key", type: "apiKey" }, {
12805
+ in: "query",
12806
+ name: "apikey",
12807
+ type: "apiKey"
12808
+ }],
12809
+ url: "/feed/v1/calendar/readarr.ics",
12810
+ ...options
12285
12811
  }), getApiV1Command2 = (options) => (options?.client ?? client4).get({
12286
12812
  security: [{ name: "X-Api-Key", type: "apiKey" }, {
12287
12813
  in: "query",
@@ -12358,6 +12884,14 @@ var getApiV1Author = (options) => (options?.client ?? client4).get({
12358
12884
  }],
12359
12885
  url: "/api/v1/customformat/schema",
12360
12886
  ...options
12887
+ }), getApiV1WantedCutoff2 = (options) => (options?.client ?? client4).get({
12888
+ security: [{ name: "X-Api-Key", type: "apiKey" }, {
12889
+ in: "query",
12890
+ name: "apikey",
12891
+ type: "apiKey"
12892
+ }],
12893
+ url: "/api/v1/wanted/cutoff",
12894
+ ...options
12361
12895
  }), getApiV1ConfigDevelopment = (options) => (options?.client ?? client4).get({
12362
12896
  security: [{ name: "X-Api-Key", type: "apiKey" }, {
12363
12897
  in: "query",
@@ -12770,6 +13304,14 @@ var getApiV1Author = (options) => (options?.client ?? client4).get({
12770
13304
  "Content-Type": "application/json",
12771
13305
  ...options.headers
12772
13306
  }
13307
+ }), getApiV1WantedMissing2 = (options) => (options?.client ?? client4).get({
13308
+ security: [{ name: "X-Api-Key", type: "apiKey" }, {
13309
+ in: "query",
13310
+ name: "apikey",
13311
+ type: "apiKey"
13312
+ }],
13313
+ url: "/api/v1/wanted/missing",
13314
+ ...options
12773
13315
  }), getApiV1ConfigNaming2 = (options) => (options?.client ?? client4).get({
12774
13316
  security: [{ name: "X-Api-Key", type: "apiKey" }, {
12775
13317
  in: "query",
@@ -13341,6 +13883,26 @@ class ReadarrClient {
13341
13883
  async searchBooks(term) {
13342
13884
  return getApiV1BookLookup({ query: { term } });
13343
13885
  }
13886
+ async getCalendar(start, end, unmonitored) {
13887
+ const query = { includeAuthor: true };
13888
+ if (start)
13889
+ query.start = start;
13890
+ if (end)
13891
+ query.end = end;
13892
+ if (unmonitored !== undefined)
13893
+ query.unmonitored = unmonitored;
13894
+ return getApiV1Calendar2(Object.keys(query).length > 0 ? { query } : {});
13895
+ }
13896
+ async getCalendarFeed(pastDays, futureDays, tagList) {
13897
+ const query = {};
13898
+ if (pastDays !== undefined)
13899
+ query.pastDays = pastDays;
13900
+ if (futureDays !== undefined)
13901
+ query.futureDays = futureDays;
13902
+ if (tagList)
13903
+ query.tagList = tagList;
13904
+ return getFeedV1CalendarReadarrIcs(Object.keys(query).length > 0 ? { query } : {});
13905
+ }
13344
13906
  async getQualityProfiles() {
13345
13907
  return getApiV1Qualityprofile2();
13346
13908
  }
@@ -13577,6 +14139,34 @@ class ReadarrClient {
13577
14139
  async removeBlocklistItemsBulk(ids) {
13578
14140
  return deleteApiV1BlocklistBulk2({ body: { ids } });
13579
14141
  }
14142
+ async getWantedMissing(page, pageSize, sortKey, sortDirection, monitored) {
14143
+ const query = { includeAuthor: true };
14144
+ if (page !== undefined)
14145
+ query.page = page;
14146
+ if (pageSize !== undefined)
14147
+ query.pageSize = pageSize;
14148
+ if (sortKey)
14149
+ query.sortKey = sortKey;
14150
+ if (sortDirection)
14151
+ query.sortDirection = sortDirection;
14152
+ if (monitored !== undefined)
14153
+ query.monitored = monitored;
14154
+ return getApiV1WantedMissing2(Object.keys(query).length > 0 ? { query } : {});
14155
+ }
14156
+ async getWantedCutoff(page, pageSize, sortKey, sortDirection, monitored) {
14157
+ const query = { includeAuthor: true };
14158
+ if (page !== undefined)
14159
+ query.page = page;
14160
+ if (pageSize !== undefined)
14161
+ query.pageSize = pageSize;
14162
+ if (sortKey)
14163
+ query.sortKey = sortKey;
14164
+ if (sortDirection)
14165
+ query.sortDirection = sortDirection;
14166
+ if (monitored !== undefined)
14167
+ query.monitored = monitored;
14168
+ return getApiV1WantedCutoff2(Object.keys(query).length > 0 ? { query } : {});
14169
+ }
13580
14170
  updateConfig(newConfig) {
13581
14171
  const updatedConfig = { ...this.clientConfig.config, ...newConfig };
13582
14172
  this.clientConfig = createServarrClient(updatedConfig);
@@ -13598,7 +14188,39 @@ var exports_readarr3 = {};
13598
14188
  __export(exports_readarr3, {
13599
14189
  readarr: () => readarr
13600
14190
  });
13601
- var resources4, readarr;
14191
+ import { readFileSync as readFileSync5 } from "node:fs";
14192
+ function unwrapData4(result) {
14193
+ return result?.data ?? result;
14194
+ }
14195
+ function formatBookListItem(book) {
14196
+ return {
14197
+ ...book,
14198
+ authorName: book?.authorName ?? book?.authorTitle ?? book?.author?.authorName ?? "—"
14199
+ };
14200
+ }
14201
+ function formatQueueListItem2(item) {
14202
+ return {
14203
+ ...item,
14204
+ authorName: item?.authorName ?? item?.authorTitle ?? item?.author?.authorName ?? "—"
14205
+ };
14206
+ }
14207
+ function formatHistoryListItem2(item) {
14208
+ return {
14209
+ ...item,
14210
+ authorName: item?.authorName ?? item?.authorTitle ?? item?.author?.authorName ?? "—"
14211
+ };
14212
+ }
14213
+ function formatBlocklistItem2(item) {
14214
+ return {
14215
+ ...item,
14216
+ authorName: item?.authorName ?? item?.authorTitle ?? item?.author?.authorName ?? "—"
14217
+ };
14218
+ }
14219
+ function readJsonInput4(filePath) {
14220
+ const raw = filePath === "-" ? readFileSync5(0, "utf-8") : readFileSync5(filePath, "utf-8");
14221
+ return JSON.parse(raw);
14222
+ }
14223
+ var resources4, readarr;
13602
14224
  var init_readarr3 = __esm(() => {
13603
14225
  init_readarr2();
13604
14226
  init_prompt2();
@@ -13615,168 +14237,459 @@ var init_readarr3 = __esm(() => {
13615
14237
  run: (c3) => c3.getAuthors()
13616
14238
  },
13617
14239
  {
13618
- name: "get",
13619
- description: "Get an author by ID",
13620
- args: [{ name: "id", description: "Author ID", required: true, type: "number" }],
13621
- run: (c3, a2) => c3.getAuthor(a2.id)
14240
+ name: "get",
14241
+ description: "Get an author by ID",
14242
+ args: [{ name: "id", description: "Author ID", required: true, type: "number" }],
14243
+ run: (c3, a2) => c3.getAuthor(a2.id)
14244
+ },
14245
+ {
14246
+ name: "search",
14247
+ description: "Search for authors",
14248
+ args: [{ name: "term", description: "Search term", required: true }],
14249
+ columns: ["foreignAuthorId", "authorName", "overview"],
14250
+ idField: "foreignAuthorId",
14251
+ run: (c3, a2) => c3.searchAuthors(a2.term)
14252
+ },
14253
+ {
14254
+ name: "add",
14255
+ description: "Search and add an author",
14256
+ args: [{ name: "term", description: "Search term", required: true }],
14257
+ run: async (c3, a2) => {
14258
+ const results = unwrapData4(await c3.searchAuthors(a2.term));
14259
+ if (!Array.isArray(results) || results.length === 0) {
14260
+ throw new Error("No authors found.");
14261
+ }
14262
+ const authorId = await promptSelect("Select an author:", results.map((author2) => ({
14263
+ label: author2.authorName,
14264
+ value: String(author2.foreignAuthorId)
14265
+ })));
14266
+ const author = results.find((item) => String(item.foreignAuthorId) === authorId);
14267
+ if (!author) {
14268
+ throw new Error("Selected author was not found in the search results.");
14269
+ }
14270
+ const profiles = unwrapData4(await c3.getQualityProfiles());
14271
+ if (!Array.isArray(profiles) || profiles.length === 0) {
14272
+ throw new Error("No quality profiles found. Configure one in Readarr first.");
14273
+ }
14274
+ const profileId = await promptSelect("Select quality profile:", profiles.map((profile) => ({ label: profile.name, value: String(profile.id) })));
14275
+ const folders = unwrapData4(await c3.getRootFolders());
14276
+ if (!Array.isArray(folders) || folders.length === 0) {
14277
+ throw new Error("No root folders found. Configure one in Readarr first.");
14278
+ }
14279
+ const rootFolderPath = await promptSelect("Select root folder:", folders.map((folder) => ({ label: folder.path, value: folder.path })));
14280
+ const confirmed = await promptConfirm(`Add "${author.authorName}"?`, !!a2.yes);
14281
+ if (!confirmed)
14282
+ throw new Error("Cancelled.");
14283
+ return c3.addAuthor({
14284
+ ...author,
14285
+ qualityProfileId: Number(profileId),
14286
+ rootFolderPath,
14287
+ monitored: true,
14288
+ addOptions: { searchForMissingBooks: true }
14289
+ });
14290
+ }
14291
+ },
14292
+ {
14293
+ name: "edit",
14294
+ description: "Edit an author",
14295
+ args: [
14296
+ { name: "id", description: "Author ID", required: true, type: "number" },
14297
+ { name: "monitored", description: "Set monitored (true/false)" },
14298
+ { name: "quality-profile-id", description: "Quality profile ID", type: "number" },
14299
+ { name: "tags", description: "Comma-separated tag IDs" }
14300
+ ],
14301
+ run: async (c3, a2) => {
14302
+ const author = unwrapData4(await c3.getAuthor(a2.id));
14303
+ const updates = { ...author };
14304
+ if (a2.monitored !== undefined)
14305
+ updates.monitored = a2.monitored === "true";
14306
+ if (a2["quality-profile-id"] !== undefined) {
14307
+ updates.qualityProfileId = Number(a2["quality-profile-id"]);
14308
+ }
14309
+ if (a2.tags !== undefined) {
14310
+ updates.tags = a2.tags.split(",").map((tag) => Number(tag.trim()));
14311
+ }
14312
+ return c3.updateAuthor(a2.id, updates);
14313
+ }
14314
+ },
14315
+ {
14316
+ name: "refresh",
14317
+ description: "Refresh author metadata",
14318
+ args: [{ name: "id", description: "Author ID", required: true, type: "number" }],
14319
+ run: (c3, a2) => c3.runCommand({ name: "RefreshAuthor", authorId: a2.id })
14320
+ },
14321
+ {
14322
+ name: "manual-search",
14323
+ description: "Trigger a manual search for releases",
14324
+ args: [{ name: "id", description: "Author ID", required: true, type: "number" }],
14325
+ run: (c3, a2) => c3.runCommand({ name: "AuthorSearch", authorId: a2.id })
14326
+ },
14327
+ {
14328
+ name: "delete",
14329
+ description: "Delete an author",
14330
+ args: [{ name: "id", description: "Author ID", required: true, type: "number" }],
14331
+ confirmMessage: "Are you sure you want to delete this author?",
14332
+ run: (c3, a2) => c3.deleteAuthor(a2.id)
14333
+ }
14334
+ ]
14335
+ },
14336
+ {
14337
+ name: "book",
14338
+ description: "Manage books",
14339
+ actions: [
14340
+ {
14341
+ name: "list",
14342
+ description: "List all books",
14343
+ columns: ["id", "authorName", "title", "monitored"],
14344
+ run: async (c3) => {
14345
+ const books = unwrapData4(await c3.getBooks());
14346
+ return books.map(formatBookListItem);
14347
+ }
14348
+ },
14349
+ {
14350
+ name: "get",
14351
+ description: "Get a book by ID",
14352
+ args: [{ name: "id", description: "Book ID", required: true, type: "number" }],
14353
+ run: async (c3, a2) => {
14354
+ const book = unwrapData4(await c3.getBook(a2.id));
14355
+ return formatBookListItem(book);
14356
+ }
14357
+ },
14358
+ {
14359
+ name: "search",
14360
+ description: "Search for books",
14361
+ args: [{ name: "term", description: "Search term", required: true }],
14362
+ columns: ["foreignBookId", "authorName", "title", "monitored"],
14363
+ idField: "foreignBookId",
14364
+ run: async (c3, a2) => {
14365
+ const books = unwrapData4(await c3.searchBooks(a2.term));
14366
+ return books.map(formatBookListItem);
14367
+ }
14368
+ },
14369
+ {
14370
+ name: "add",
14371
+ description: "Add a book from JSON file or stdin",
14372
+ args: [{ name: "file", description: "JSON file path (use - for stdin)", required: true }],
14373
+ run: async (c3, a2) => c3.addBook(readJsonInput4(a2.file))
14374
+ },
14375
+ {
14376
+ name: "edit",
14377
+ description: "Edit a book (merges JSON with existing)",
14378
+ args: [
14379
+ { name: "id", description: "Book ID", required: true, type: "number" },
14380
+ { name: "file", description: "JSON file with fields to update", required: true }
14381
+ ],
14382
+ run: async (c3, a2) => {
14383
+ const existing = unwrapData4(await c3.getBook(a2.id));
14384
+ return c3.updateBook(a2.id, { ...existing, ...readJsonInput4(a2.file) });
14385
+ }
14386
+ },
14387
+ {
14388
+ name: "delete",
14389
+ description: "Delete a book",
14390
+ args: [{ name: "id", description: "Book ID", required: true, type: "number" }],
14391
+ confirmMessage: "Are you sure you want to delete this book?",
14392
+ run: (c3, a2) => c3.deleteBook(a2.id)
14393
+ }
14394
+ ]
14395
+ },
14396
+ {
14397
+ name: "profile",
14398
+ description: "Manage quality profiles",
14399
+ actions: [
14400
+ {
14401
+ name: "list",
14402
+ description: "List quality profiles",
14403
+ columns: ["id", "name"],
14404
+ run: (c3) => c3.getQualityProfiles()
14405
+ },
14406
+ {
14407
+ name: "get",
14408
+ description: "Get a quality profile by ID",
14409
+ args: [{ name: "id", description: "Profile ID", required: true, type: "number" }],
14410
+ run: (c3, a2) => c3.getQualityProfile(a2.id)
14411
+ }
14412
+ ]
14413
+ },
14414
+ {
14415
+ name: "tag",
14416
+ description: "Manage tags",
14417
+ actions: [
14418
+ {
14419
+ name: "create",
14420
+ description: "Create a tag",
14421
+ args: [{ name: "label", description: "Tag label", required: true }],
14422
+ run: (c3, a2) => c3.addTag({ label: a2.label })
14423
+ },
14424
+ {
14425
+ name: "delete",
14426
+ description: "Delete a tag",
14427
+ args: [{ name: "id", description: "Tag ID", required: true, type: "number" }],
14428
+ confirmMessage: "Are you sure you want to delete this tag?",
14429
+ run: async (c3, a2) => {
14430
+ const tagResult = await c3.getTag(a2.id);
14431
+ if (tagResult?.error)
14432
+ return tagResult;
14433
+ const tag = unwrapData4(tagResult);
14434
+ const deleteResult = await c3.deleteTag(a2.id);
14435
+ if (deleteResult?.error)
14436
+ return deleteResult;
14437
+ return { message: `Deleted tag: ${tag.label} (ID: ${tag.id})` };
14438
+ }
14439
+ },
14440
+ {
14441
+ name: "list",
14442
+ description: "List all tags",
14443
+ columns: ["id", "label"],
14444
+ run: (c3) => c3.getTags()
14445
+ }
14446
+ ]
14447
+ },
14448
+ {
14449
+ name: "queue",
14450
+ description: "Manage download queue",
14451
+ actions: [
14452
+ {
14453
+ name: "list",
14454
+ description: "List queue items",
14455
+ columns: ["id", "authorName", "title", "status", "sizeleft", "timeleft"],
14456
+ run: async (c3) => {
14457
+ const items = unwrapData4(await c3.getQueue());
14458
+ return items.map(formatQueueListItem2);
14459
+ }
14460
+ },
14461
+ {
14462
+ name: "status",
14463
+ description: "Get queue status",
14464
+ run: (c3) => c3.getQueueStatus()
14465
+ },
14466
+ {
14467
+ name: "delete",
14468
+ description: "Remove an item from the queue",
14469
+ args: [
14470
+ { name: "id", description: "Queue item ID", required: true, type: "number" },
14471
+ { name: "blocklist", description: "Add to blocklist", type: "boolean" },
14472
+ {
14473
+ name: "remove-from-client",
14474
+ description: "Remove from download client",
14475
+ type: "boolean"
14476
+ }
14477
+ ],
14478
+ confirmMessage: "Are you sure you want to remove this queue item?",
14479
+ run: (c3, a2) => c3.removeQueueItem(a2.id, a2["remove-from-client"], a2.blocklist)
14480
+ },
14481
+ {
14482
+ name: "grab",
14483
+ description: "Force download a queue item",
14484
+ args: [{ name: "id", description: "Queue item ID", required: true, type: "number" }],
14485
+ run: (c3, a2) => c3.grabQueueItem(a2.id)
14486
+ }
14487
+ ]
14488
+ },
14489
+ {
14490
+ name: "history",
14491
+ description: "View history",
14492
+ actions: [
14493
+ {
14494
+ name: "list",
14495
+ description: "List recent history",
14496
+ args: [
14497
+ { name: "since", description: "Start date (ISO 8601, e.g. 2024-01-01)" },
14498
+ { name: "until", description: "End date (ISO 8601, e.g. 2024-12-31)" }
14499
+ ],
14500
+ columns: ["id", "authorName", "sourceTitle", "eventType", "date"],
14501
+ run: async (c3, a2) => {
14502
+ const items = a2.since ? unwrapData4(await c3.getHistorySince(a2.since)) : unwrapData4(await c3.getHistory());
14503
+ const filtered = a2.until ? items.filter((item) => new Date(item.date) <= new Date(a2.until)) : items;
14504
+ return filtered.map(formatHistoryListItem2);
14505
+ }
14506
+ }
14507
+ ]
14508
+ },
14509
+ {
14510
+ name: "calendar",
14511
+ description: "View upcoming releases",
14512
+ actions: [
14513
+ {
14514
+ name: "list",
14515
+ description: "List upcoming book releases",
14516
+ args: [
14517
+ { name: "start", description: "Start date (ISO 8601)" },
14518
+ { name: "end", description: "End date (ISO 8601)" },
14519
+ { name: "unmonitored", description: "Include unmonitored", type: "boolean" }
14520
+ ],
14521
+ columns: ["id", "authorName", "title", "releaseDate"],
14522
+ run: async (c3, a2) => {
14523
+ const books = unwrapData4(await c3.getCalendar(a2.start, a2.end, a2.unmonitored));
14524
+ return books.map(formatBookListItem);
14525
+ }
14526
+ }
14527
+ ]
14528
+ },
14529
+ {
14530
+ name: "notification",
14531
+ description: "Manage notifications",
14532
+ actions: [
14533
+ {
14534
+ name: "list",
14535
+ description: "List notification providers",
14536
+ columns: ["id", "name", "implementation"],
14537
+ run: (c3) => c3.getNotifications()
13622
14538
  },
13623
14539
  {
13624
- name: "search",
13625
- description: "Search for authors",
13626
- args: [{ name: "term", description: "Search term", required: true }],
13627
- columns: ["foreignAuthorId", "authorName", "overview"],
13628
- run: (c3, a2) => c3.searchAuthors(a2.term)
14540
+ name: "get",
14541
+ description: "Get a notification by ID",
14542
+ args: [{ name: "id", description: "Notification ID", required: true, type: "number" }],
14543
+ run: (c3, a2) => c3.getNotification(a2.id)
13629
14544
  },
13630
14545
  {
13631
14546
  name: "add",
13632
- description: "Search and add an author",
13633
- args: [{ name: "term", description: "Search term", required: true }],
13634
- run: async (c3, a2) => {
13635
- const searchResult = await c3.searchAuthors(a2.term);
13636
- const results = searchResult?.data ?? searchResult;
13637
- if (!Array.isArray(results) || results.length === 0) {
13638
- throw new Error("No authors found.");
13639
- }
13640
- const authorId = await promptSelect("Select an author:", results.map((au) => ({
13641
- label: au.authorName,
13642
- value: String(au.foreignAuthorId)
13643
- })));
13644
- const author = results.find((au) => String(au.foreignAuthorId) === authorId);
13645
- const profilesResult = await c3.getQualityProfiles();
13646
- const profiles = profilesResult?.data ?? profilesResult;
13647
- if (!Array.isArray(profiles) || profiles.length === 0) {
13648
- throw new Error("No quality profiles found. Configure one in Readarr first.");
13649
- }
13650
- const profileId = await promptSelect("Select quality profile:", profiles.map((p) => ({ label: p.name, value: String(p.id) })));
13651
- const foldersResult = await c3.getRootFolders();
13652
- const folders = foldersResult?.data ?? foldersResult;
13653
- if (!Array.isArray(folders) || folders.length === 0) {
13654
- throw new Error("No root folders found. Configure one in Readarr first.");
13655
- }
13656
- const rootFolderPath = await promptSelect("Select root folder:", folders.map((f3) => ({ label: f3.path, value: f3.path })));
13657
- const confirmed = await promptConfirm(`Add "${author.authorName}"?`, !!a2.yes);
13658
- if (!confirmed)
13659
- throw new Error("Cancelled.");
13660
- return c3.addAuthor({
13661
- ...author,
13662
- qualityProfileId: Number(profileId),
13663
- rootFolderPath,
13664
- monitored: true,
13665
- addOptions: { searchForMissingBooks: true }
13666
- });
13667
- }
14547
+ description: "Add a notification from JSON file or stdin",
14548
+ args: [{ name: "file", description: "JSON file path (use - for stdin)", required: true }],
14549
+ run: async (c3, a2) => c3.addNotification(readJsonInput4(a2.file))
13668
14550
  },
13669
14551
  {
13670
14552
  name: "edit",
13671
- description: "Edit an author",
14553
+ description: "Edit a notification (merges JSON with existing)",
13672
14554
  args: [
13673
- { name: "id", description: "Author ID", required: true, type: "number" },
13674
- { name: "monitored", description: "Set monitored (true/false)" },
13675
- { name: "quality-profile-id", description: "Quality profile ID", type: "number" },
13676
- { name: "tags", description: "Comma-separated tag IDs" }
14555
+ { name: "id", description: "Notification ID", required: true, type: "number" },
14556
+ { name: "file", description: "JSON file with fields to update", required: true }
13677
14557
  ],
13678
14558
  run: async (c3, a2) => {
13679
- const result = await c3.getAuthor(a2.id);
13680
- const author = result?.data ?? result;
13681
- const updates = { ...author };
13682
- if (a2.monitored !== undefined)
13683
- updates.monitored = a2.monitored === "true";
13684
- if (a2["quality-profile-id"] !== undefined)
13685
- updates.qualityProfileId = Number(a2["quality-profile-id"]);
13686
- if (a2.tags !== undefined)
13687
- updates.tags = a2.tags.split(",").map((t2) => Number(t2.trim()));
13688
- return c3.updateAuthor(a2.id, updates);
14559
+ const existing = unwrapData4(await c3.getNotification(a2.id));
14560
+ return c3.updateNotification(a2.id, { ...existing, ...readJsonInput4(a2.file) });
13689
14561
  }
13690
14562
  },
13691
14563
  {
13692
- name: "refresh",
13693
- description: "Refresh author metadata",
13694
- args: [{ name: "id", description: "Author ID", required: true, type: "number" }],
13695
- run: (c3, a2) => c3.runCommand({ name: "RefreshAuthor", authorId: a2.id })
13696
- },
13697
- {
13698
- name: "manual-search",
13699
- description: "Trigger a manual search for releases",
13700
- args: [{ name: "id", description: "Author ID", required: true, type: "number" }],
13701
- run: (c3, a2) => c3.runCommand({ name: "AuthorSearch", authorId: a2.id })
14564
+ name: "delete",
14565
+ description: "Delete a notification",
14566
+ args: [{ name: "id", description: "Notification ID", required: true, type: "number" }],
14567
+ confirmMessage: "Are you sure you want to delete this notification?",
14568
+ run: (c3, a2) => c3.deleteNotification(a2.id)
13702
14569
  },
13703
14570
  {
13704
- name: "delete",
13705
- description: "Delete an author",
13706
- args: [{ name: "id", description: "Author ID", required: true, type: "number" }],
13707
- confirmMessage: "Are you sure you want to delete this author?",
13708
- run: (c3, a2) => c3.deleteAuthor(a2.id)
14571
+ name: "test",
14572
+ description: "Test all notifications",
14573
+ run: (c3) => c3.testAllNotifications()
13709
14574
  }
13710
14575
  ]
13711
14576
  },
13712
14577
  {
13713
- name: "book",
13714
- description: "Manage books",
14578
+ name: "downloadclient",
14579
+ description: "Manage download clients",
13715
14580
  actions: [
13716
14581
  {
13717
14582
  name: "list",
13718
- description: "List all books",
13719
- columns: ["id", "title", "authorId", "monitored"],
13720
- run: (c3) => c3.getBooks()
14583
+ description: "List download clients",
14584
+ columns: ["id", "name", "implementation", "enable"],
14585
+ run: (c3) => c3.getDownloadClients()
13721
14586
  },
13722
14587
  {
13723
14588
  name: "get",
13724
- description: "Get a book by ID",
13725
- args: [{ name: "id", description: "Book ID", required: true, type: "number" }],
13726
- run: (c3, a2) => c3.getBook(a2.id)
14589
+ description: "Get a download client by ID",
14590
+ args: [{ name: "id", description: "Download client ID", required: true, type: "number" }],
14591
+ run: (c3, a2) => c3.getDownloadClient(a2.id)
13727
14592
  },
13728
14593
  {
13729
- name: "search",
13730
- description: "Search for books",
13731
- args: [{ name: "term", description: "Search term", required: true }],
13732
- columns: ["foreignBookId", "title", "authorId"],
13733
- run: (c3, a2) => c3.searchBooks(a2.term)
14594
+ name: "add",
14595
+ description: "Add a download client from JSON file or stdin",
14596
+ args: [{ name: "file", description: "JSON file path (use - for stdin)", required: true }],
14597
+ run: async (c3, a2) => c3.addDownloadClient(readJsonInput4(a2.file))
14598
+ },
14599
+ {
14600
+ name: "edit",
14601
+ description: "Edit a download client (merges JSON with existing)",
14602
+ args: [
14603
+ { name: "id", description: "Download client ID", required: true, type: "number" },
14604
+ { name: "file", description: "JSON file with fields to update", required: true }
14605
+ ],
14606
+ run: async (c3, a2) => {
14607
+ const existing = unwrapData4(await c3.getDownloadClient(a2.id));
14608
+ return c3.updateDownloadClient(a2.id, { ...existing, ...readJsonInput4(a2.file) });
14609
+ }
14610
+ },
14611
+ {
14612
+ name: "delete",
14613
+ description: "Delete a download client",
14614
+ args: [{ name: "id", description: "Download client ID", required: true, type: "number" }],
14615
+ confirmMessage: "Are you sure you want to delete this download client?",
14616
+ run: (c3, a2) => c3.deleteDownloadClient(a2.id)
14617
+ },
14618
+ {
14619
+ name: "test",
14620
+ description: "Test all download clients",
14621
+ run: (c3) => c3.testAllDownloadClients()
13734
14622
  }
13735
14623
  ]
13736
14624
  },
13737
14625
  {
13738
- name: "profile",
13739
- description: "Manage quality profiles",
14626
+ name: "blocklist",
14627
+ description: "View blocked releases",
13740
14628
  actions: [
13741
14629
  {
13742
14630
  name: "list",
13743
- description: "List quality profiles",
13744
- columns: ["id", "name"],
13745
- run: (c3) => c3.getQualityProfiles()
14631
+ description: "List blocked releases",
14632
+ columns: ["id", "authorName", "sourceTitle", "date"],
14633
+ run: async (c3) => {
14634
+ const items = unwrapData4(await c3.getBlocklist());
14635
+ return items.map(formatBlocklistItem2);
14636
+ }
14637
+ },
14638
+ {
14639
+ name: "delete",
14640
+ description: "Remove a release from the blocklist",
14641
+ args: [{ name: "id", description: "Blocklist item ID", required: true, type: "number" }],
14642
+ confirmMessage: "Are you sure you want to remove this blocklist entry?",
14643
+ run: (c3, a2) => c3.removeBlocklistItem(a2.id)
13746
14644
  }
13747
14645
  ]
13748
14646
  },
13749
14647
  {
13750
- name: "tag",
13751
- description: "Manage tags",
14648
+ name: "wanted",
14649
+ description: "View missing and cutoff unmet books",
13752
14650
  actions: [
13753
14651
  {
13754
- name: "create",
13755
- description: "Create a tag",
13756
- args: [{ name: "label", description: "Tag label", required: true }],
13757
- run: (c3, a2) => c3.addTag({ label: a2.label })
14652
+ name: "missing",
14653
+ description: "List books with missing files",
14654
+ columns: ["id", "authorName", "title", "releaseDate"],
14655
+ run: async (c3) => {
14656
+ const books = unwrapData4(await c3.getWantedMissing());
14657
+ return books.map(formatBookListItem);
14658
+ }
13758
14659
  },
13759
14660
  {
13760
- name: "delete",
13761
- description: "Delete a tag",
13762
- args: [{ name: "id", description: "Tag ID", required: true, type: "number" }],
13763
- confirmMessage: "Are you sure you want to delete this tag?",
13764
- run: async (c3, a2) => {
13765
- const tagResult = await c3.getTag(a2.id);
13766
- if (tagResult?.error)
13767
- return tagResult;
13768
- const tag = tagResult?.data ?? tagResult;
13769
- const deleteResult = await c3.deleteTag(a2.id);
13770
- if (deleteResult?.error)
13771
- return deleteResult;
13772
- return { message: `Deleted tag: ${tag.label} (ID: ${tag.id})` };
14661
+ name: "cutoff",
14662
+ description: "List books below quality cutoff",
14663
+ columns: ["id", "authorName", "title", "releaseDate"],
14664
+ run: async (c3) => {
14665
+ const books = unwrapData4(await c3.getWantedCutoff());
14666
+ return books.map(formatBookListItem);
13773
14667
  }
13774
- },
14668
+ }
14669
+ ]
14670
+ },
14671
+ {
14672
+ name: "importlist",
14673
+ description: "Manage import lists",
14674
+ actions: [
13775
14675
  {
13776
14676
  name: "list",
13777
- description: "List all tags",
13778
- columns: ["id", "label"],
13779
- run: (c3) => c3.getTags()
14677
+ description: "List import lists",
14678
+ columns: ["id", "name", "implementation", "enable"],
14679
+ run: (c3) => c3.getImportLists()
14680
+ },
14681
+ {
14682
+ name: "get",
14683
+ description: "Get an import list by ID",
14684
+ args: [{ name: "id", description: "Import list ID", required: true, type: "number" }],
14685
+ run: (c3, a2) => c3.getImportList(a2.id)
14686
+ },
14687
+ {
14688
+ name: "delete",
14689
+ description: "Delete an import list",
14690
+ args: [{ name: "id", description: "Import list ID", required: true, type: "number" }],
14691
+ confirmMessage: "Are you sure you want to delete this import list?",
14692
+ run: (c3, a2) => c3.deleteImportList(a2.id)
13780
14693
  }
13781
14694
  ]
13782
14695
  },
@@ -15446,6 +16359,53 @@ var exports_prowlarr3 = {};
15446
16359
  __export(exports_prowlarr3, {
15447
16360
  prowlarr: () => prowlarr
15448
16361
  });
16362
+ import { readFileSync as readFileSync6 } from "node:fs";
16363
+ function unwrapData5(result) {
16364
+ return result?.data ?? result;
16365
+ }
16366
+ function readJsonInput5(filePath) {
16367
+ const raw = filePath === "-" ? readFileSync6(0, "utf-8") : readFileSync6(filePath, "utf-8");
16368
+ return JSON.parse(raw);
16369
+ }
16370
+ async function runIndexerTest(client6, indexer) {
16371
+ const result = await client6.testIndexer(indexer);
16372
+ if (result?.error) {
16373
+ const error = result.error;
16374
+ const response = result.response;
16375
+ const status = error?.status ?? response?.status;
16376
+ return {
16377
+ id: indexer?.id,
16378
+ name: indexer?.name ?? "Unknown indexer",
16379
+ status: "fail",
16380
+ message: error?.title ?? error?.message ?? `API error (${status ?? "unknown"})`
16381
+ };
16382
+ }
16383
+ const data = unwrapData5(result);
16384
+ return {
16385
+ id: indexer?.id,
16386
+ name: indexer?.name ?? "Unknown indexer",
16387
+ status: "ok",
16388
+ message: extractIndexerTestMessage(data)
16389
+ };
16390
+ }
16391
+ function extractIndexerTestMessage(data) {
16392
+ if (typeof data === "string" && data.trim() !== "") {
16393
+ return data;
16394
+ }
16395
+ if (Array.isArray(data) && data.length > 0) {
16396
+ const first = data[0];
16397
+ if (typeof first === "string" && first.trim() !== "") {
16398
+ return first;
16399
+ }
16400
+ if (typeof first?.message === "string" && first.message.trim() !== "") {
16401
+ return first.message;
16402
+ }
16403
+ }
16404
+ if (typeof data?.message === "string" && data.message.trim() !== "") {
16405
+ return data.message;
16406
+ }
16407
+ return "Test completed";
16408
+ }
15449
16409
  var resources5, prowlarr;
15450
16410
  var init_prowlarr3 = __esm(() => {
15451
16411
  init_prowlarr2();
@@ -15468,6 +16428,28 @@ var init_prowlarr3 = __esm(() => {
15468
16428
  args: [{ name: "id", description: "Indexer ID", required: true, type: "number" }],
15469
16429
  run: (c3, a2) => c3.getIndexer(a2.id)
15470
16430
  },
16431
+ {
16432
+ name: "add",
16433
+ description: "Add an indexer from JSON file or stdin",
16434
+ args: [{ name: "file", description: "JSON file path (use - for stdin)", required: true }],
16435
+ run: async (c3, a2) => {
16436
+ const body = readJsonInput5(a2.file);
16437
+ return c3.addIndexer(body);
16438
+ }
16439
+ },
16440
+ {
16441
+ name: "edit",
16442
+ description: "Edit an indexer (merges JSON with existing)",
16443
+ args: [
16444
+ { name: "id", description: "Indexer ID", required: true, type: "number" },
16445
+ { name: "file", description: "JSON file with fields to update", required: true }
16446
+ ],
16447
+ run: async (c3, a2) => {
16448
+ const existing = unwrapData5(await c3.getIndexer(a2.id));
16449
+ const updates = readJsonInput5(a2.file);
16450
+ return c3.updateIndexer(a2.id, { ...existing, ...updates });
16451
+ }
16452
+ },
15471
16453
  {
15472
16454
  name: "delete",
15473
16455
  description: "Delete an indexer",
@@ -15477,8 +16459,17 @@ var init_prowlarr3 = __esm(() => {
15477
16459
  },
15478
16460
  {
15479
16461
  name: "test",
15480
- description: "Test all indexers",
15481
- run: (c3) => c3.testAllIndexers()
16462
+ description: "Test one indexer or all configured indexers",
16463
+ args: [{ name: "id", description: "Indexer ID", type: "number" }],
16464
+ columns: ["id", "name", "status", "message"],
16465
+ run: async (c3, a2) => {
16466
+ const indexers = a2.id ? [unwrapData5(await c3.getIndexer(a2.id))] : unwrapData5(await c3.getIndexers());
16467
+ const results = [];
16468
+ for (const indexer of indexers) {
16469
+ results.push(await runIndexerTest(c3, indexer));
16470
+ }
16471
+ return a2.id ? results[0] : results;
16472
+ }
15482
16473
  }
15483
16474
  ]
15484
16475
  },
@@ -15514,6 +16505,28 @@ var init_prowlarr3 = __esm(() => {
15514
16505
  args: [{ name: "id", description: "Application ID", required: true, type: "number" }],
15515
16506
  run: (c3, a2) => c3.getApplication(a2.id)
15516
16507
  },
16508
+ {
16509
+ name: "add",
16510
+ description: "Add an application from JSON file or stdin",
16511
+ args: [{ name: "file", description: "JSON file path (use - for stdin)", required: true }],
16512
+ run: async (c3, a2) => {
16513
+ const body = readJsonInput5(a2.file);
16514
+ return c3.addApplication(body);
16515
+ }
16516
+ },
16517
+ {
16518
+ name: "edit",
16519
+ description: "Edit an application (merges JSON with existing)",
16520
+ args: [
16521
+ { name: "id", description: "Application ID", required: true, type: "number" },
16522
+ { name: "file", description: "JSON file with fields to update", required: true }
16523
+ ],
16524
+ run: async (c3, a2) => {
16525
+ const existing = unwrapData5(await c3.getApplication(a2.id));
16526
+ const updates = readJsonInput5(a2.file);
16527
+ return c3.updateApplication(a2.id, { ...existing, ...updates });
16528
+ }
16529
+ },
15517
16530
  {
15518
16531
  name: "delete",
15519
16532
  description: "Delete an application",
@@ -15569,7 +16582,17 @@ var init_prowlarr3 = __esm(() => {
15569
16582
  {
15570
16583
  name: "list",
15571
16584
  description: "Get indexer performance statistics",
15572
- run: (c3) => c3.getIndexerStats()
16585
+ columns: [
16586
+ "indexerName",
16587
+ "numberOfQueries",
16588
+ "numberOfGrabs",
16589
+ "numberOfFailures",
16590
+ "averageResponseTime"
16591
+ ],
16592
+ run: async (c3) => {
16593
+ const result = unwrapData5(await c3.getIndexerStats());
16594
+ return result?.indexers ?? result;
16595
+ }
15573
16596
  }
15574
16597
  ]
15575
16598
  },
@@ -15589,6 +16612,24 @@ var init_prowlarr3 = __esm(() => {
15589
16612
  args: [{ name: "id", description: "Notification ID", required: true, type: "number" }],
15590
16613
  run: (c3, a2) => c3.getNotification(a2.id)
15591
16614
  },
16615
+ {
16616
+ name: "add",
16617
+ description: "Add a notification from JSON file or stdin",
16618
+ args: [{ name: "file", description: "JSON file path (use - for stdin)", required: true }],
16619
+ run: async (c3, a2) => c3.addNotification(readJsonInput5(a2.file))
16620
+ },
16621
+ {
16622
+ name: "edit",
16623
+ description: "Edit a notification (merges JSON with existing)",
16624
+ args: [
16625
+ { name: "id", description: "Notification ID", required: true, type: "number" },
16626
+ { name: "file", description: "JSON file with fields to update", required: true }
16627
+ ],
16628
+ run: async (c3, a2) => {
16629
+ const existing = unwrapData5(await c3.getNotification(a2.id));
16630
+ return c3.updateNotification(a2.id, { ...existing, ...readJsonInput5(a2.file) });
16631
+ }
16632
+ },
15592
16633
  {
15593
16634
  name: "delete",
15594
16635
  description: "Delete a notification",
@@ -15619,6 +16660,24 @@ var init_prowlarr3 = __esm(() => {
15619
16660
  args: [{ name: "id", description: "Download client ID", required: true, type: "number" }],
15620
16661
  run: (c3, a2) => c3.getDownloadClient(a2.id)
15621
16662
  },
16663
+ {
16664
+ name: "add",
16665
+ description: "Add a download client from JSON file or stdin",
16666
+ args: [{ name: "file", description: "JSON file path (use - for stdin)", required: true }],
16667
+ run: async (c3, a2) => c3.addDownloadClient(readJsonInput5(a2.file))
16668
+ },
16669
+ {
16670
+ name: "edit",
16671
+ description: "Edit a download client (merges JSON with existing)",
16672
+ args: [
16673
+ { name: "id", description: "Download client ID", required: true, type: "number" },
16674
+ { name: "file", description: "JSON file with fields to update", required: true }
16675
+ ],
16676
+ run: async (c3, a2) => {
16677
+ const existing = unwrapData5(await c3.getDownloadClient(a2.id));
16678
+ return c3.updateDownloadClient(a2.id, { ...existing, ...readJsonInput5(a2.file) });
16679
+ }
16680
+ },
15622
16681
  {
15623
16682
  name: "delete",
15624
16683
  description: "Delete a download client",
@@ -16762,6 +17821,12 @@ function getBazarrApiBaseUrl(baseUrl) {
16762
17821
  const normalizedBaseUrl = baseUrl.replace(/\/$/, "");
16763
17822
  return normalizedBaseUrl.endsWith("/api") ? normalizedBaseUrl : `${normalizedBaseUrl}/api`;
16764
17823
  }
17824
+ function getBazarrHeaders(config) {
17825
+ return {
17826
+ "Content-Type": "application/json",
17827
+ ...config.config.headers ?? {}
17828
+ };
17829
+ }
16765
17830
 
16766
17831
  class BazarrClient {
16767
17832
  clientConfig;
@@ -16769,7 +17834,8 @@ class BazarrClient {
16769
17834
  this.clientConfig = createServarrClient(config);
16770
17835
  client6.setConfig({
16771
17836
  baseUrl: getBazarrApiBaseUrl(this.clientConfig.getBaseUrl()),
16772
- headers: this.clientConfig.getHeaders()
17837
+ headers: getBazarrHeaders(this.clientConfig),
17838
+ auth: this.clientConfig.config.apiKey
16773
17839
  });
16774
17840
  }
16775
17841
  async getSystemStatus() {
@@ -17109,7 +18175,8 @@ class BazarrClient {
17109
18175
  this.clientConfig = createServarrClient(updatedConfig);
17110
18176
  client6.setConfig({
17111
18177
  baseUrl: getBazarrApiBaseUrl(this.clientConfig.getBaseUrl()),
17112
- headers: this.clientConfig.getHeaders()
18178
+ headers: getBazarrHeaders(this.clientConfig),
18179
+ auth: this.clientConfig.config.apiKey
17113
18180
  });
17114
18181
  return this.clientConfig.config;
17115
18182
  }
@@ -17751,8 +18818,8 @@ var init_completions = __esm(() => {
17751
18818
 
17752
18819
  // src/cli/index.ts
17753
18820
  init_dist();
17754
- import { readFileSync as readFileSync2 } from "node:fs";
17755
- var { version } = JSON.parse(readFileSync2(new URL("../../package.json", import.meta.url), "utf-8"));
18821
+ import { readFileSync as readFileSync7 } from "node:fs";
18822
+ var { version } = JSON.parse(readFileSync7(new URL("../../package.json", import.meta.url), "utf-8"));
17756
18823
  var main = defineCommand({
17757
18824
  meta: {
17758
18825
  name: "tsarr",