async function replacePDF(item) {
  // filter annotations, attachments, notes
  if (!item.isRegularItem()) {
    return;
  }
  let fileExists = [];
  let oldPDF = null;
  // filter multiple or existing PDFs
  const attachmentIDs = item.getAttachments();
  for (let itemID of attachmentIDs) {
    const attachment = Zotero.Items.get(itemID);
    if (!attachment.isPDFAttachment()) {
      continue;
    }
    oldPDF = attachment;
    const exists = await attachment.fileExists();
    fileExists.push(exists);
  }
  if (fileExists.length > 1) {
    return; // multiple PDFs found
  }
  if (fileExists.pop()) {
    return; // PDF already exists
  }
  console.log("Updating PDF for", item.getDisplayTitle());
  // manually invoke "Find Available PDF"
  const newPDF = await Zotero.Attachments.addAvailablePDF(item);
  if (oldPDF) {
    await OS.File.move(newPDF.getFilePath(), oldPDF.getFilePath());
    await newPDF.eraseTx();
  }
}

// loop replacePDF() over all items in our library
const libraryID = Zotero.Libraries.userLibraryID;
let items = await Zotero.Items.getAll(libraryID);

for (let item of items) {
  await replacePDF(item);
}