{
  "version": 3,
  "sources": ["../../../bin/fetch-log.ts", "../../../crypto.ts", "../../../bin/find-root.ts"],
  "sourcesContent": ["#!/usr/bin/env node\n\nimport { Command } from 'commander'\nimport crypto, { webcrypto } from 'crypto'\nimport fs from 'fs'\nimport * as jose from 'jose'\nimport StreamZip from 'node-stream-zip'\nimport { Readable } from 'node:stream'\nimport { finished } from 'node:stream/promises'\nimport path from 'path'\n\nimport { Entry as KeyRingEntry } from '@napi-rs/keyring'\nimport prompts from 'prompts'\n\nimport { KEY_WRAPPING_ALG } from '../crypto'\nimport { pkg } from './find-root'\n\ntype FetchLogOptions = {\n  private: string\n  keep: boolean\n  encrypted: boolean\n  zip: string\n  url: string\n}\n\ntype LogIdGroups = {\n  host: string\n  key: string\n  tags: string\n}\n\nasync function getPassphrase(): Promise<string> {\n  const service = `${pkg.name} Zotero plugin`\n  const account = `${pkg.name}-debug-log`\n  const entry = new KeyRingEntry(service, account)\n  const storedPassphrase = entry.getPassword()\n  if (storedPassphrase) return storedPassphrase\n\n  const response = await prompts({\n    type: 'password',\n    name: 'passphrase',\n    message: `Enter a passphrase to decrypt your private key for ${service} ${account}:`,\n  }) as { passphrase?: string }\n  const providedPassphrase = response.passphrase || oops('No passphrase entered')\n\n  entry.setPassword(providedPassphrase)\n  return providedPassphrase\n}\n\nconst oops = (...args: unknown[]): never => {\n  console.error(...args)\n  process.exit(1)\n}\n\nconst program = new Command()\nprogram\n  .description('A script to fetch debug logs.')\n  .option('-p, --private <path>', 'Path for the encrypted private key .pem file', 'private.pem')\n  .option('-k, --keep', 'Keep the downloaded zip', false)\n  .argument('<debug log id>', 'debug log ID to fetch')\n  .parse(process.argv)\nconst args = program.args as string[]\n\nif (!args.length) oops('No log ID')\n\nconst m = args[0].match(/^(?<key>[a-z0-9]+)-(?<host>[^.-]+)(?<tags>([.][^.]+)*)$/i)\nconst groups = m?.groups as Partial<LogIdGroups> | undefined\nif (!groups?.host || !groups.key || typeof groups.tags !== 'string') oops(args[0], 'is not a valid log ID')\n\nconst { host, key, tags } = groups as LogIdGroups\nif (host !== 'fbin') oops('Unexpected debug log host', host)\n\nconst options: FetchLogOptions = {\n  ...(program.opts() as Pick<FetchLogOptions, 'private' | 'keep'>),\n  encrypted: tags.split('.').includes('enc'),\n  zip: path.join('logs', `${key}.zip`),\n  url: `https://filebin.net/${key}/${key}.zip`,\n}\n\nif (options.encrypted) {\n  if (!options.private) oops('No private key provided')\n  if (!fs.existsSync(options.private)) oops('Private key', options.private, 'does not exist')\n  if (!fs.existsSync('package.json')) oops('package.json does not exist in the current directory')\n}\n\nconst logs = path.join('logs', key)\nconsole.log(options.url, '=>', logs)\nif (!fs.existsSync(logs)) {\n  fs.mkdirSync(logs, { recursive: true })\n}\n\nasync function getPrivateKey(): Promise<webcrypto.CryptoKey | undefined> {\n  if (!options.encrypted) return undefined\n\n  const privateKeyObject = crypto.createPrivateKey({\n    key: fs.readFileSync(options.private, 'utf-8'),\n    format: 'pem',\n    passphrase: await getPassphrase(),\n  })\n  const unencryptedKeyPEM = privateKeyObject.export({\n    type: 'pkcs8',\n    format: 'pem',\n  }).toString()\n  return await jose.importPKCS8(\n    unencryptedKeyPEM,\n    KEY_WRAPPING_ALG,\n  )\n}\nasync function main() {\n  try {\n    const response = await fetch(options.url, {\n      method: 'GET',\n      headers: {\n        'User-Agent': 'curl/7.81.0', // filebin does not seem to accept custom user agents\n        'Accept-Encoding': 'identity',\n        Accept: '*/*',\n      },\n    })\n    if (!response.ok) oops(`Failed to download: ${response.statusText}`)\n\n    const download = fs.createWriteStream(options.zip)\n    await finished(Readable.fromWeb(response.body as any).pipe(download))\n\n    const zipfile = new StreamZip.async({ file: options.zip })\n    const entries = Object.values(await zipfile.entries()).filter(entry => !entry.isDirectory)\n\n    const privateKey = await getPrivateKey()\n\n    for (const entry of entries) {\n      const m = entry.name.match(/(?<filename>.+)\\.(?<type>jwe)$/i)\n      const filename = m?.groups!.filename || entry.name\n      const type = (m?.groups!.type || '').toLowerCase()\n      const target = path.join('logs', filename)\n\n      if (options.encrypted && !type) oops('Unexpected unencrypted contents', entry.name)\n      if (type && !options.encrypted) oops('Unexpected encrypted contents', entry.name)\n      switch (type) {\n        case '':\n          fs.writeFileSync(target, await zipfile.entryData(entry.name))\n          break\n        case 'jwe': {\n          if (!privateKey) oops('Missing private key for encrypted log entry', entry.name)\n          const { plaintext } = await jose.compactDecrypt((await zipfile.entryData(entry.name)).toString('utf8'), privateKey)\n          fs.writeFileSync(target, plaintext)\n          break\n        }\n        default:\n          oops('Unexpected log entry', entry.name)\n          break\n      }\n    }\n  }\n  finally {\n    if (!options.keep && fs.existsSync(options.zip)) fs.unlinkSync(options.zip)\n  }\n}\n\nmain().catch(err => {\n  oops(err.message)\n})\n", "export const KEYTYPE = 'RSA'\nexport const KEY_WRAPPING_ALG = 'RSA-OAEP-256'\nexport const CONTENT_ENCRYPTION_ALG = 'A256GCM'\nexport const RSA_HASH = 'SHA-256'\nexport const RSA_ALGORITHM = 'RSA-OAEP'\nexport const CIPHER_ALGORITHM = 'aes256'\nexport const AES_KEY_LENGTH = 256\n// export const AES_KEY_BYTES = 32\n\n// export const AES_ALGORITHM = 'AES-GCM'\n// export const IV_LENGTH = 12\n", "import fs from 'node:fs'\nimport path from 'node:path'\n\nconst folders = process.cwd().split(path.sep)\nconst rootIndex = folders.findIndex((folder: string, i: number) => fs.existsSync(path.join(folders.slice(0, i + 1).join(path.sep), 'package.json')))\nexport const root = (rootIndex > 0 ? folders.slice(0, rootIndex + 1) : folders).join(path.sep)\nconsole.log('project directory:', root)\n\nexport const pkg = JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf-8'))\n"],
  "mappings": "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",
  "names": ["path", "fs", "KeyRingEntry", "prompts", "args", "path", "fs", "crypto", "StreamZip", "m"]
}
