/ clis / paperreview / submit.js
submit.js
 1  import { cli, Strategy } from '@jackwener/opencli/registry';
 2  import { CliError } from '@jackwener/opencli/errors';
 3  import { PAPERREVIEW_DOMAIN, ensureApiSuccess, ensureSuccess, normalizeVenue, readPdfFile, requestJson, summarizeSubmission, uploadPresignedPdf, } from './utils.js';
 4  cli({
 5      site: 'paperreview',
 6      name: 'submit',
 7      description: 'Submit a PDF to paperreview.ai for review',
 8      domain: PAPERREVIEW_DOMAIN,
 9      strategy: Strategy.PUBLIC,
10      browser: false,
11      timeoutSeconds: 120,
12      args: [
13          { name: 'pdf', positional: true, required: true, help: 'Path to the paper PDF' },
14          { name: 'email', required: true, help: 'Email address for the submission' },
15          { name: 'venue', help: 'Optional target venue such as ICLR or NeurIPS' },
16          { name: 'dry-run', type: 'bool', default: false, help: 'Validate the input and stop before remote submission' },
17          { name: 'prepare-only', type: 'bool', default: false, help: 'Request an upload slot but stop before uploading the PDF' },
18      ],
19      columns: ['status', 'file', 'email', 'venue', 'token', 'review_url', 'message'],
20      footerExtra: (kwargs) => {
21          if (kwargs['dry-run'] === true)
22              return 'dry run only';
23          if (kwargs['prepare-only'] === true)
24              return 'prepared only';
25          return undefined;
26      },
27      func: async (_page, kwargs) => {
28          const pdfFile = await readPdfFile(kwargs.pdf);
29          const email = String(kwargs.email ?? '').trim();
30          const venue = normalizeVenue(kwargs.venue);
31          const dryRun = kwargs['dry-run'] === true;
32          const prepareOnly = kwargs['prepare-only'] === true;
33          if (!email) {
34              throw new CliError('ARGUMENT', 'An email address is required.', 'Pass --email <address>');
35          }
36          if (dryRun) {
37              return summarizeSubmission({
38                  pdfFile,
39                  email,
40                  venue,
41                  message: 'Input validation passed. No remote request was sent.',
42                  dryRun: true,
43              });
44          }
45          const { response: uploadUrlResponse, payload: uploadUrlPayload } = await requestJson('/api/get-upload-url', {
46              method: 'POST',
47              headers: { 'Content-Type': 'application/json' },
48              body: JSON.stringify({
49                  filename: pdfFile.fileName,
50                  venue,
51              }),
52          });
53          ensureSuccess(uploadUrlResponse, uploadUrlPayload, 'Failed to request an upload URL.', 'Try again in a moment');
54          ensureApiSuccess(uploadUrlPayload, 'paperreview.ai did not return a usable upload URL.', 'Try again in a moment');
55          if (prepareOnly) {
56              return summarizeSubmission({
57                  pdfFile,
58                  email,
59                  venue,
60                  message: 'Upload slot prepared. The PDF was not uploaded and no submission was confirmed.',
61                  s3Key: uploadUrlPayload.s3_key,
62                  status: 'prepared',
63              });
64          }
65          await uploadPresignedPdf(uploadUrlPayload.presigned_url, pdfFile, uploadUrlPayload);
66          const confirmForm = new FormData();
67          confirmForm.append('s3_key', uploadUrlPayload.s3_key);
68          confirmForm.append('venue', venue);
69          confirmForm.append('email', email);
70          const { response: confirmResponse, payload: confirmPayload } = await requestJson('/api/confirm-upload', {
71              method: 'POST',
72              body: confirmForm,
73          });
74          ensureSuccess(confirmResponse, confirmPayload, 'Failed to confirm the upload with paperreview.ai.', 'Try again in a moment');
75          ensureApiSuccess(confirmPayload, 'paperreview.ai did not confirm the submission.', 'Try again in a moment');
76          return summarizeSubmission({
77              pdfFile,
78              email,
79              venue,
80              token: confirmPayload.token,
81              message: confirmPayload.message,
82              s3Key: uploadUrlPayload.s3_key,
83          });
84      },
85  });