@ncukondo/reference-manager 0.20.0 → 0.22.0
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/chunks/{action-menu-Brg5Lmz7.js → action-menu-B1DCdkkp.js} +4 -3
- package/dist/chunks/{action-menu-Brg5Lmz7.js.map → action-menu-B1DCdkkp.js.map} +1 -1
- package/dist/chunks/{format-CYO99JV-.js → format-CfTZqhUx.js} +2 -2
- package/dist/chunks/{format-CYO99JV-.js.map → format-CfTZqhUx.js.map} +1 -1
- package/dist/chunks/index-BK3hTiKR.js +5929 -0
- package/dist/chunks/index-BK3hTiKR.js.map +1 -0
- package/dist/chunks/{index-D--7n1SB.js → index-BvIfOciH.js} +8 -5
- package/dist/chunks/index-BvIfOciH.js.map +1 -0
- package/dist/chunks/{index-BR5tlrNU.js → index-D7eiOplw.js} +3 -2
- package/dist/chunks/index-D7eiOplw.js.map +1 -0
- package/dist/chunks/{index-Cno7_aWr.js → index-DOvEusHb.js} +447 -426
- package/dist/chunks/index-DOvEusHb.js.map +1 -0
- package/dist/chunks/{loader-BtW20O32.js → loader-BHvcats5.js} +113 -27
- package/dist/chunks/loader-BHvcats5.js.map +1 -0
- package/dist/chunks/{reference-select-DrINWBuP.js → reference-select-DTqhevya.js} +3 -3
- package/dist/chunks/{reference-select-DrINWBuP.js.map → reference-select-DTqhevya.js.map} +1 -1
- package/dist/chunks/{style-select-DxcSWBSF.js → style-select-CXclvgJO.js} +3 -3
- package/dist/chunks/{style-select-DxcSWBSF.js.map → style-select-CXclvgJO.js.map} +1 -1
- package/dist/cli/commands/add.d.ts +20 -0
- package/dist/cli/commands/add.d.ts.map +1 -1
- package/dist/cli/commands/fulltext.d.ts +86 -3
- package/dist/cli/commands/fulltext.d.ts.map +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli.js +1 -1
- package/dist/config/defaults.d.ts.map +1 -1
- package/dist/config/env-override.d.ts.map +1 -1
- package/dist/config/key-parser.d.ts.map +1 -1
- package/dist/config/loader.d.ts.map +1 -1
- package/dist/config/schema.d.ts +65 -0
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/features/format/pretty.d.ts.map +1 -1
- package/dist/features/format/resource-indicators.d.ts +8 -0
- package/dist/features/format/resource-indicators.d.ts.map +1 -0
- package/dist/features/interactive/apps/runSearchFlow.d.ts +5 -0
- package/dist/features/interactive/apps/runSearchFlow.d.ts.map +1 -1
- package/dist/features/operations/fulltext/convert.d.ts +22 -0
- package/dist/features/operations/fulltext/convert.d.ts.map +1 -0
- package/dist/features/operations/fulltext/discover.d.ts +35 -0
- package/dist/features/operations/fulltext/discover.d.ts.map +1 -0
- package/dist/features/operations/fulltext/fetch.d.ts +34 -0
- package/dist/features/operations/fulltext/fetch.d.ts.map +1 -0
- package/dist/features/operations/fulltext/index.d.ts +3 -0
- package/dist/features/operations/fulltext/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/mcp/tools/fulltext.d.ts +37 -0
- package/dist/mcp/tools/fulltext.d.ts.map +1 -1
- package/dist/mcp/tools/index.d.ts.map +1 -1
- package/dist/server/routes/references.d.ts +3 -1
- package/dist/server/routes/references.d.ts.map +1 -1
- package/dist/server.js +2 -2
- package/package.json +2 -1
- package/dist/chunks/index-BR5tlrNU.js.map +0 -1
- package/dist/chunks/index-Cno7_aWr.js.map +0 -1
- package/dist/chunks/index-D--7n1SB.js.map +0 -1
- package/dist/chunks/index-DrZawbND.js +0 -2082
- package/dist/chunks/index-DrZawbND.js.map +0 -1
- package/dist/chunks/loader-BtW20O32.js.map +0 -1
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
2
|
import { i as isEqual, M as MANAGED_CUSTOM_FIELDS, L as Library, g as CslItemSchema, p as pickDefined, a as sortOrderSchema, x as paginationOptionsSchema, F as FileWatcher, b as sortFieldSchema, v as searchSortFieldSchema } from "./file-watcher-CrsNHUpz.js";
|
|
3
3
|
import * as fs from "node:fs";
|
|
4
|
-
import { promises, readFileSync, existsSync, writeFileSync, mkdirSync
|
|
4
|
+
import { promises, readFileSync, existsSync, writeFileSync, mkdirSync } from "node:fs";
|
|
5
5
|
import * as os from "node:os";
|
|
6
|
-
import { tmpdir } from "node:os";
|
|
7
6
|
import * as path from "node:path";
|
|
8
|
-
import path__default, {
|
|
9
|
-
import {
|
|
10
|
-
import
|
|
11
|
-
import { o as openWithSystemApp, l as loadConfig, e as getDefaultCurrentDirConfigFilename, h as getDefaultUserConfigPath } from "./loader-
|
|
7
|
+
import path__default, { join, basename, dirname } from "node:path";
|
|
8
|
+
import { n as normalizePathForOutput, d as deleteDirectoryIfEmpty, p as parseFilename, i as isReservedRole, e as ensureDirectory, a as addAttachment, R as RESERVED_ROLES, g as generateFilename, b as findFulltextFiles, c as findFulltextFile, h as extensionToFormat, j as fulltextAttach, k as fulltextGet, l as fulltextDiscover, m as fulltextFetch, o as fulltextConvert, q as getExtension, B as BUILTIN_STYLES, r as getFulltextAttachmentTypes, s as startServerWithFileWatcher } from "./index-BK3hTiKR.js";
|
|
9
|
+
import { readFile, unlink, stat, readdir, rename, mkdir } from "node:fs/promises";
|
|
10
|
+
import { o as openWithSystemApp, l as loadConfig, e as getDefaultCurrentDirConfigFilename, h as getDefaultUserConfigPath } from "./loader-BHvcats5.js";
|
|
12
11
|
import { spawn, spawnSync } from "node:child_process";
|
|
13
12
|
import process$1, { stdin, stdout } from "node:process";
|
|
14
13
|
import * as readline from "node:readline";
|
|
@@ -20,7 +19,7 @@ import "@citation-js/plugin-csl";
|
|
|
20
19
|
import { ZodOptional as ZodOptional$2, z } from "zod";
|
|
21
20
|
import { serve } from "@hono/node-server";
|
|
22
21
|
const name = "@ncukondo/reference-manager";
|
|
23
|
-
const version$1 = "0.
|
|
22
|
+
const version$1 = "0.22.0";
|
|
24
23
|
const description$1 = "A local reference management tool using CSL-JSON as the single source of truth";
|
|
25
24
|
const packageJson = {
|
|
26
25
|
name,
|
|
@@ -414,216 +413,28 @@ function getExitCode(result) {
|
|
|
414
413
|
}
|
|
415
414
|
return 0;
|
|
416
415
|
}
|
|
417
|
-
function
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
function generateFilename(role, ext, label) {
|
|
421
|
-
if (label) {
|
|
422
|
-
const slug = slugifyLabel(label);
|
|
423
|
-
return `${role}-${slug}.${ext}`;
|
|
424
|
-
}
|
|
425
|
-
return `${role}.${ext}`;
|
|
426
|
-
}
|
|
427
|
-
function parseFilename(filename) {
|
|
428
|
-
if (!filename) {
|
|
429
|
-
return null;
|
|
430
|
-
}
|
|
431
|
-
const ext = path__default.extname(filename);
|
|
432
|
-
const extWithoutDot = ext.startsWith(".") ? ext.slice(1) : ext;
|
|
433
|
-
const basename2 = ext ? filename.slice(0, -ext.length) : filename;
|
|
434
|
-
const firstHyphenIndex = basename2.indexOf("-");
|
|
435
|
-
if (firstHyphenIndex === -1) {
|
|
436
|
-
return {
|
|
437
|
-
role: basename2,
|
|
438
|
-
ext: extWithoutDot
|
|
439
|
-
};
|
|
440
|
-
}
|
|
441
|
-
const role = basename2.slice(0, firstHyphenIndex);
|
|
442
|
-
const label = basename2.slice(firstHyphenIndex + 1);
|
|
443
|
-
if (label) {
|
|
444
|
-
return {
|
|
445
|
-
role,
|
|
446
|
-
ext: extWithoutDot,
|
|
447
|
-
label
|
|
448
|
-
};
|
|
449
|
-
}
|
|
450
|
-
return {
|
|
451
|
-
role,
|
|
452
|
-
ext: extWithoutDot
|
|
453
|
-
};
|
|
454
|
-
}
|
|
455
|
-
function normalizePathForOutput(p) {
|
|
456
|
-
return p.replace(/\\/g, "/");
|
|
457
|
-
}
|
|
458
|
-
function extractUuidPrefix(uuid2) {
|
|
459
|
-
const normalized = uuid2.replace(/-/g, "");
|
|
460
|
-
return normalized.slice(0, 8);
|
|
461
|
-
}
|
|
462
|
-
function generateDirectoryName(ref2) {
|
|
463
|
-
const uuid2 = ref2.custom?.uuid;
|
|
464
|
-
if (!uuid2) {
|
|
465
|
-
throw new Error("Reference must have custom.uuid");
|
|
466
|
-
}
|
|
467
|
-
const uuidPrefix = extractUuidPrefix(uuid2);
|
|
468
|
-
const pmid = ref2.PMID?.trim();
|
|
469
|
-
if (pmid) {
|
|
470
|
-
return `${ref2.id}-PMID${pmid}-${uuidPrefix}`;
|
|
471
|
-
}
|
|
472
|
-
return `${ref2.id}-${uuidPrefix}`;
|
|
473
|
-
}
|
|
474
|
-
function getDirectoryPath(ref2, baseDir) {
|
|
475
|
-
const existingDir = ref2.custom?.attachments?.directory;
|
|
476
|
-
if (existingDir) {
|
|
477
|
-
return normalizePathForOutput(path__default.join(baseDir, existingDir));
|
|
478
|
-
}
|
|
479
|
-
const dirName = generateDirectoryName(ref2);
|
|
480
|
-
return normalizePathForOutput(path__default.join(baseDir, dirName));
|
|
481
|
-
}
|
|
482
|
-
async function ensureDirectory(ref2, baseDir) {
|
|
483
|
-
const dirPath = getDirectoryPath(ref2, baseDir);
|
|
484
|
-
try {
|
|
485
|
-
await fs__default.mkdir(dirPath, { recursive: true });
|
|
486
|
-
} catch (error) {
|
|
487
|
-
if (error.code !== "EEXIST") {
|
|
488
|
-
throw error;
|
|
489
|
-
}
|
|
490
|
-
}
|
|
491
|
-
return dirPath;
|
|
492
|
-
}
|
|
493
|
-
async function deleteDirectoryIfEmpty(dirPath) {
|
|
494
|
-
try {
|
|
495
|
-
const files = await fs__default.readdir(dirPath);
|
|
496
|
-
if (files.length === 0) {
|
|
497
|
-
await fs__default.rmdir(dirPath);
|
|
498
|
-
}
|
|
499
|
-
} catch (error) {
|
|
500
|
-
if (error.code !== "ENOENT") {
|
|
501
|
-
throw error;
|
|
502
|
-
}
|
|
503
|
-
}
|
|
504
|
-
}
|
|
505
|
-
async function checkSourceFile(filePath) {
|
|
506
|
-
try {
|
|
507
|
-
await stat(filePath);
|
|
508
|
-
return null;
|
|
509
|
-
} catch {
|
|
510
|
-
return `Source file not found: ${filePath}`;
|
|
511
|
-
}
|
|
512
|
-
}
|
|
513
|
-
function validateFulltextConstraint(existingFiles, newFile) {
|
|
514
|
-
if (newFile.role !== "fulltext") {
|
|
515
|
-
return null;
|
|
516
|
-
}
|
|
517
|
-
const newExt = getExtension(newFile.filename);
|
|
518
|
-
const existingFulltexts = existingFiles.filter((f) => f.role === "fulltext");
|
|
519
|
-
for (const existing of existingFulltexts) {
|
|
520
|
-
const existingExt = getExtension(existing.filename);
|
|
521
|
-
if (existingExt === newExt) {
|
|
522
|
-
return `A fulltext ${newExt.toUpperCase()} already exists. Use --force to overwrite.`;
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
const testFiles = [...existingFiles, newFile];
|
|
526
|
-
if (!isValidFulltextFiles(testFiles)) {
|
|
527
|
-
return "fulltext role allows max 2 files (1 PDF + 1 Markdown)";
|
|
528
|
-
}
|
|
529
|
-
return null;
|
|
530
|
-
}
|
|
531
|
-
function findExistingFile(files, filename) {
|
|
532
|
-
return files.find((f) => f.filename === filename);
|
|
533
|
-
}
|
|
534
|
-
async function copyOrMoveFile(sourcePath, destPath, move) {
|
|
535
|
-
try {
|
|
536
|
-
if (move) {
|
|
537
|
-
await rename(sourcePath, destPath);
|
|
538
|
-
} else {
|
|
539
|
-
await copyFile(sourcePath, destPath);
|
|
540
|
-
}
|
|
541
|
-
return null;
|
|
542
|
-
} catch (error) {
|
|
543
|
-
return `Failed to ${move ? "move" : "copy"} file: ${error instanceof Error ? error.message : String(error)}`;
|
|
544
|
-
}
|
|
545
|
-
}
|
|
546
|
-
async function updateAttachmentMetadata$1(library, item, updatedAttachments) {
|
|
547
|
-
await library.update(item.id, {
|
|
548
|
-
custom: {
|
|
549
|
-
...item.custom,
|
|
550
|
-
attachments: updatedAttachments
|
|
551
|
-
}
|
|
552
|
-
});
|
|
553
|
-
}
|
|
554
|
-
function buildUpdatedFiles$1(existingFiles, newFile, existingFile) {
|
|
555
|
-
if (existingFile) {
|
|
556
|
-
return existingFiles.map((f) => f.filename === newFile.filename ? newFile : f);
|
|
557
|
-
}
|
|
558
|
-
return [...existingFiles, newFile];
|
|
559
|
-
}
|
|
560
|
-
async function addAttachment(library, options) {
|
|
561
|
-
const {
|
|
562
|
-
identifier,
|
|
563
|
-
filePath,
|
|
564
|
-
role,
|
|
565
|
-
label,
|
|
566
|
-
move = false,
|
|
567
|
-
force = false,
|
|
568
|
-
idType = "id",
|
|
569
|
-
attachmentsDirectory
|
|
570
|
-
} = options;
|
|
571
|
-
const item = await library.find(identifier, { idType });
|
|
572
|
-
if (!item) {
|
|
573
|
-
return { success: false, error: `Reference '${identifier}' not found` };
|
|
574
|
-
}
|
|
575
|
-
const uuid2 = item.custom?.uuid;
|
|
576
|
-
if (!uuid2) {
|
|
577
|
-
return { success: false, error: "Reference has no UUID. Cannot create attachment directory." };
|
|
578
|
-
}
|
|
579
|
-
const sourceError = await checkSourceFile(filePath);
|
|
580
|
-
if (sourceError) {
|
|
581
|
-
return { success: false, error: sourceError };
|
|
416
|
+
async function autoFetchFulltext(addedItems, context, options) {
|
|
417
|
+
if (addedItems.length === 0) {
|
|
418
|
+
return [];
|
|
582
419
|
}
|
|
583
|
-
const
|
|
584
|
-
const
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
420
|
+
const results = [];
|
|
421
|
+
for (const item of addedItems) {
|
|
422
|
+
try {
|
|
423
|
+
const result = await options.fulltextFetchFn(context.library, {
|
|
424
|
+
identifier: item.id,
|
|
425
|
+
idType: "id",
|
|
426
|
+
fulltextConfig: options.fulltextConfig,
|
|
427
|
+
fulltextDirectory: options.fulltextDirectory
|
|
428
|
+
});
|
|
429
|
+
results.push(result);
|
|
430
|
+
} catch (error) {
|
|
431
|
+
results.push({
|
|
432
|
+
success: false,
|
|
433
|
+
error: error instanceof Error ? error.message : String(error)
|
|
434
|
+
});
|
|
597
435
|
}
|
|
598
436
|
}
|
|
599
|
-
|
|
600
|
-
return {
|
|
601
|
-
success: false,
|
|
602
|
-
existingFile: filename,
|
|
603
|
-
requiresConfirmation: true
|
|
604
|
-
};
|
|
605
|
-
}
|
|
606
|
-
const ref2 = item;
|
|
607
|
-
const dirPath = await ensureDirectory(ref2, attachmentsDirectory);
|
|
608
|
-
const dirName = existingAttachments?.directory ?? generateDirectoryName(ref2);
|
|
609
|
-
const destPath = join(dirPath, filename);
|
|
610
|
-
const copyError = await copyOrMoveFile(filePath, destPath, move);
|
|
611
|
-
if (copyError) {
|
|
612
|
-
return { success: false, error: copyError };
|
|
613
|
-
}
|
|
614
|
-
const updatedFiles = buildUpdatedFiles$1(existingFiles, newFile, existingFile);
|
|
615
|
-
const updatedAttachments = {
|
|
616
|
-
directory: dirName,
|
|
617
|
-
files: updatedFiles
|
|
618
|
-
};
|
|
619
|
-
await updateAttachmentMetadata$1(library, item, updatedAttachments);
|
|
620
|
-
await library.save();
|
|
621
|
-
return {
|
|
622
|
-
success: true,
|
|
623
|
-
filename,
|
|
624
|
-
directory: dirName,
|
|
625
|
-
overwritten: !!existingFile
|
|
626
|
-
};
|
|
437
|
+
return results;
|
|
627
438
|
}
|
|
628
439
|
async function listAttachments(library, options) {
|
|
629
440
|
const { identifier, idType = "id", role } = options;
|
|
@@ -1093,15 +904,15 @@ class OperationsLibrary {
|
|
|
1093
904
|
}
|
|
1094
905
|
// High-level operations
|
|
1095
906
|
async search(options) {
|
|
1096
|
-
const { searchReferences } = await import("./index-
|
|
907
|
+
const { searchReferences } = await import("./index-BK3hTiKR.js").then((n) => n.z);
|
|
1097
908
|
return searchReferences(this.library, options);
|
|
1098
909
|
}
|
|
1099
910
|
async list(options) {
|
|
1100
|
-
const { listReferences } = await import("./index-
|
|
911
|
+
const { listReferences } = await import("./index-BK3hTiKR.js").then((n) => n.y);
|
|
1101
912
|
return listReferences(this.library, options ?? {});
|
|
1102
913
|
}
|
|
1103
914
|
async cite(options) {
|
|
1104
|
-
const { citeReferences } = await import("./index-
|
|
915
|
+
const { citeReferences } = await import("./index-BK3hTiKR.js").then((n) => n.x);
|
|
1105
916
|
const defaultStyle = options.defaultStyle ?? this.citationConfig?.defaultStyle;
|
|
1106
917
|
const cslDirectory = options.cslDirectory ?? this.citationConfig?.cslDirectory;
|
|
1107
918
|
const mergedOptions = {
|
|
@@ -1112,32 +923,32 @@ class OperationsLibrary {
|
|
|
1112
923
|
return citeReferences(this.library, mergedOptions);
|
|
1113
924
|
}
|
|
1114
925
|
async import(inputs, options) {
|
|
1115
|
-
const { addReferences } = await import("./index-
|
|
926
|
+
const { addReferences } = await import("./index-BK3hTiKR.js").then((n) => n.w);
|
|
1116
927
|
return addReferences(inputs, this.library, options ?? {});
|
|
1117
928
|
}
|
|
1118
929
|
// Attachment operations
|
|
1119
930
|
async attachAdd(options) {
|
|
1120
|
-
const { addAttachment: addAttachment2 } = await import("./index-
|
|
931
|
+
const { addAttachment: addAttachment2 } = await import("./index-D7eiOplw.js");
|
|
1121
932
|
return addAttachment2(this.library, options);
|
|
1122
933
|
}
|
|
1123
934
|
async attachList(options) {
|
|
1124
|
-
const { listAttachments: listAttachments2 } = await import("./index-
|
|
935
|
+
const { listAttachments: listAttachments2 } = await import("./index-D7eiOplw.js");
|
|
1125
936
|
return listAttachments2(this.library, options);
|
|
1126
937
|
}
|
|
1127
938
|
async attachGet(options) {
|
|
1128
|
-
const { getAttachment: getAttachment2 } = await import("./index-
|
|
939
|
+
const { getAttachment: getAttachment2 } = await import("./index-D7eiOplw.js");
|
|
1129
940
|
return getAttachment2(this.library, options);
|
|
1130
941
|
}
|
|
1131
942
|
async attachDetach(options) {
|
|
1132
|
-
const { detachAttachment: detachAttachment2 } = await import("./index-
|
|
943
|
+
const { detachAttachment: detachAttachment2 } = await import("./index-D7eiOplw.js");
|
|
1133
944
|
return detachAttachment2(this.library, options);
|
|
1134
945
|
}
|
|
1135
946
|
async attachSync(options) {
|
|
1136
|
-
const { syncAttachments: syncAttachments2 } = await import("./index-
|
|
947
|
+
const { syncAttachments: syncAttachments2 } = await import("./index-D7eiOplw.js");
|
|
1137
948
|
return syncAttachments2(this.library, options);
|
|
1138
949
|
}
|
|
1139
950
|
async attachOpen(options) {
|
|
1140
|
-
const { openAttachment: openAttachment2 } = await import("./index-
|
|
951
|
+
const { openAttachment: openAttachment2 } = await import("./index-D7eiOplw.js");
|
|
1141
952
|
return openAttachment2(this.library, options);
|
|
1142
953
|
}
|
|
1143
954
|
}
|
|
@@ -1981,7 +1792,7 @@ function getAttachExitCode(result) {
|
|
|
1981
1792
|
}
|
|
1982
1793
|
async function executeInteractiveSelect$2(context, config2) {
|
|
1983
1794
|
const { withAlternateScreen: withAlternateScreen2 } = await Promise.resolve().then(() => alternateScreen);
|
|
1984
|
-
const { selectReferencesOrExit } = await import("./reference-select-
|
|
1795
|
+
const { selectReferencesOrExit } = await import("./reference-select-DTqhevya.js");
|
|
1985
1796
|
const allReferences = await context.library.getAll();
|
|
1986
1797
|
const identifiers = await withAlternateScreen2(
|
|
1987
1798
|
() => selectReferencesOrExit(allReferences, { multiSelect: false }, config2.cli.tui)
|
|
@@ -2529,8 +2340,8 @@ function getCiteExitCode(result) {
|
|
|
2529
2340
|
}
|
|
2530
2341
|
async function executeInteractiveCite(options, context, config2) {
|
|
2531
2342
|
const { withAlternateScreen: withAlternateScreen2 } = await Promise.resolve().then(() => alternateScreen);
|
|
2532
|
-
const { runCiteFlow } = await import("./index-
|
|
2533
|
-
const { buildStyleChoices, listCustomStyles } = await import("./style-select-
|
|
2343
|
+
const { runCiteFlow } = await import("./index-BvIfOciH.js");
|
|
2344
|
+
const { buildStyleChoices, listCustomStyles } = await import("./style-select-CXclvgJO.js");
|
|
2534
2345
|
const { search } = await import("./file-watcher-CrsNHUpz.js").then((n) => n.z);
|
|
2535
2346
|
const { tokenize } = await import("./file-watcher-CrsNHUpz.js").then((n) => n.y);
|
|
2536
2347
|
const { checkTTY } = await import("./tty-BMyaEOhX.js");
|
|
@@ -2615,7 +2426,9 @@ const ENV_OVERRIDE_MAP = {
|
|
|
2615
2426
|
REFERENCE_MANAGER_MCP_DEFAULT_LIMIT: "mcp.default_limit",
|
|
2616
2427
|
REFERENCE_MANAGER_CLIPBOARD_AUTO_COPY: "cli.tui.clipboard_auto_copy",
|
|
2617
2428
|
PUBMED_EMAIL: "pubmed.email",
|
|
2618
|
-
PUBMED_API_KEY: "pubmed.api_key"
|
|
2429
|
+
PUBMED_API_KEY: "pubmed.api_key",
|
|
2430
|
+
UNPAYWALL_EMAIL: "fulltext.sources.unpaywall_email",
|
|
2431
|
+
CORE_API_KEY: "fulltext.sources.core_api_key"
|
|
2619
2432
|
};
|
|
2620
2433
|
const KEY_TO_ENV_MAP = Object.fromEntries(
|
|
2621
2434
|
Object.entries(ENV_OVERRIDE_MAP).map(([envVar, configKey]) => [configKey, envVar])
|
|
@@ -2679,6 +2492,29 @@ const CONFIG_KEY_REGISTRY = [
|
|
|
2679
2492
|
// pubmed section
|
|
2680
2493
|
{ key: "pubmed.email", type: "string", description: "Email for PubMed API", optional: true },
|
|
2681
2494
|
{ key: "pubmed.api_key", type: "string", description: "API key for PubMed", optional: true },
|
|
2495
|
+
// fulltext section
|
|
2496
|
+
{
|
|
2497
|
+
key: "fulltext.prefer_sources",
|
|
2498
|
+
type: "string[]",
|
|
2499
|
+
description: "Fulltext source priority order"
|
|
2500
|
+
},
|
|
2501
|
+
{
|
|
2502
|
+
key: "fulltext.auto_fetch_on_add",
|
|
2503
|
+
type: "boolean",
|
|
2504
|
+
description: "Auto-fetch fulltext when adding references"
|
|
2505
|
+
},
|
|
2506
|
+
{
|
|
2507
|
+
key: "fulltext.sources.unpaywall_email",
|
|
2508
|
+
type: "string",
|
|
2509
|
+
description: "Email for Unpaywall API",
|
|
2510
|
+
optional: true
|
|
2511
|
+
},
|
|
2512
|
+
{
|
|
2513
|
+
key: "fulltext.sources.core_api_key",
|
|
2514
|
+
type: "string",
|
|
2515
|
+
description: "API key for CORE",
|
|
2516
|
+
optional: true
|
|
2517
|
+
},
|
|
2682
2518
|
// attachments section
|
|
2683
2519
|
{ key: "attachments.directory", type: "string", description: "Attachments storage directory" },
|
|
2684
2520
|
// cli section
|
|
@@ -6478,7 +6314,7 @@ function writeBlockMapping(state, level, object2, compact) {
|
|
|
6478
6314
|
state.tag = _tag;
|
|
6479
6315
|
state.dump = _result || "{}";
|
|
6480
6316
|
}
|
|
6481
|
-
function detectType
|
|
6317
|
+
function detectType(state, object2, explicit) {
|
|
6482
6318
|
var _result, typeList, index, length, type2, style;
|
|
6483
6319
|
typeList = explicit ? state.explicitTypes : state.implicitTypes;
|
|
6484
6320
|
for (index = 0, length = typeList.length; index < length; index += 1) {
|
|
@@ -6512,8 +6348,8 @@ function detectType$1(state, object2, explicit) {
|
|
|
6512
6348
|
function writeNode(state, level, object2, block, compact, iskey, isblockseq) {
|
|
6513
6349
|
state.tag = null;
|
|
6514
6350
|
state.dump = object2;
|
|
6515
|
-
if (!detectType
|
|
6516
|
-
detectType
|
|
6351
|
+
if (!detectType(state, object2, false)) {
|
|
6352
|
+
detectType(state, object2, true);
|
|
6517
6353
|
}
|
|
6518
6354
|
var type2 = _toString.call(state.dump);
|
|
6519
6355
|
var inblock = block;
|
|
@@ -7087,7 +6923,7 @@ function formatEditOutput(result) {
|
|
|
7087
6923
|
}
|
|
7088
6924
|
async function executeInteractiveEdit(options, context, config2) {
|
|
7089
6925
|
const { withAlternateScreen: withAlternateScreen2 } = await Promise.resolve().then(() => alternateScreen);
|
|
7090
|
-
const { selectReferencesOrExit } = await import("./reference-select-
|
|
6926
|
+
const { selectReferencesOrExit } = await import("./reference-select-DTqhevya.js");
|
|
7091
6927
|
const allReferences = await context.library.getAll();
|
|
7092
6928
|
const identifiers = await withAlternateScreen2(
|
|
7093
6929
|
() => selectReferencesOrExit(allReferences, { multiSelect: true }, config2.cli.tui)
|
|
@@ -10346,188 +10182,6 @@ function formatExportOutput(result, options) {
|
|
|
10346
10182
|
function getExportExitCode(result) {
|
|
10347
10183
|
return result.notFound.length > 0 ? 1 : 0;
|
|
10348
10184
|
}
|
|
10349
|
-
function detectType(filePath) {
|
|
10350
|
-
const ext = extname(filePath).toLowerCase();
|
|
10351
|
-
if (ext === ".pdf") return "pdf";
|
|
10352
|
-
if (ext === ".md" || ext === ".markdown") return "markdown";
|
|
10353
|
-
return void 0;
|
|
10354
|
-
}
|
|
10355
|
-
function resolveFileType(explicitType, filePath, stdinContent) {
|
|
10356
|
-
let fileType = explicitType;
|
|
10357
|
-
if (!fileType && filePath) {
|
|
10358
|
-
fileType = detectType(filePath);
|
|
10359
|
-
}
|
|
10360
|
-
if (stdinContent && !fileType) {
|
|
10361
|
-
return {
|
|
10362
|
-
error: "File type must be specified with --pdf or --markdown when reading from stdin."
|
|
10363
|
-
};
|
|
10364
|
-
}
|
|
10365
|
-
if (!fileType) {
|
|
10366
|
-
return { error: "Cannot detect file type. Use --pdf or --markdown to specify the type." };
|
|
10367
|
-
}
|
|
10368
|
-
return fileType;
|
|
10369
|
-
}
|
|
10370
|
-
function prepareStdinSource(stdinContent, fileType) {
|
|
10371
|
-
try {
|
|
10372
|
-
const tempDir = mkdtempSync(join(tmpdir(), "refmgr-"));
|
|
10373
|
-
const ext = formatToExtension(fileType);
|
|
10374
|
-
const sourcePath = join(tempDir, `stdin.${ext}`);
|
|
10375
|
-
writeFileSync(sourcePath, stdinContent);
|
|
10376
|
-
return { sourcePath, tempDir };
|
|
10377
|
-
} catch (error) {
|
|
10378
|
-
return {
|
|
10379
|
-
error: `Failed to write stdin content: ${error instanceof Error ? error.message : String(error)}`
|
|
10380
|
-
};
|
|
10381
|
-
}
|
|
10382
|
-
}
|
|
10383
|
-
async function cleanupTempDir(tempDir) {
|
|
10384
|
-
if (tempDir) {
|
|
10385
|
-
await rm(tempDir, { recursive: true, force: true }).catch(() => {
|
|
10386
|
-
});
|
|
10387
|
-
}
|
|
10388
|
-
}
|
|
10389
|
-
function prepareSourcePath(filePath, stdinContent, fileType) {
|
|
10390
|
-
if (stdinContent) {
|
|
10391
|
-
return prepareStdinSource(stdinContent, fileType);
|
|
10392
|
-
}
|
|
10393
|
-
if (!filePath) {
|
|
10394
|
-
return { error: "No file path or stdin content provided." };
|
|
10395
|
-
}
|
|
10396
|
-
return { sourcePath: filePath };
|
|
10397
|
-
}
|
|
10398
|
-
function convertResult(result, fileType) {
|
|
10399
|
-
if (result.success) {
|
|
10400
|
-
return {
|
|
10401
|
-
success: true,
|
|
10402
|
-
filename: result.filename,
|
|
10403
|
-
type: fileType,
|
|
10404
|
-
overwritten: result.overwritten
|
|
10405
|
-
};
|
|
10406
|
-
}
|
|
10407
|
-
if (result.requiresConfirmation) {
|
|
10408
|
-
return {
|
|
10409
|
-
success: false,
|
|
10410
|
-
existingFile: result.existingFile,
|
|
10411
|
-
requiresConfirmation: true
|
|
10412
|
-
};
|
|
10413
|
-
}
|
|
10414
|
-
return {
|
|
10415
|
-
success: false,
|
|
10416
|
-
error: result.error
|
|
10417
|
-
};
|
|
10418
|
-
}
|
|
10419
|
-
async function fulltextAttach(library, options) {
|
|
10420
|
-
const {
|
|
10421
|
-
identifier,
|
|
10422
|
-
filePath,
|
|
10423
|
-
type: explicitType,
|
|
10424
|
-
move,
|
|
10425
|
-
force,
|
|
10426
|
-
idType = "id",
|
|
10427
|
-
fulltextDirectory,
|
|
10428
|
-
stdinContent
|
|
10429
|
-
} = options;
|
|
10430
|
-
const fileTypeResult = resolveFileType(explicitType, filePath, stdinContent);
|
|
10431
|
-
if (typeof fileTypeResult === "object" && "error" in fileTypeResult) {
|
|
10432
|
-
const item = await library.find(identifier, { idType });
|
|
10433
|
-
if (!item) {
|
|
10434
|
-
return { success: false, error: `Reference '${identifier}' not found` };
|
|
10435
|
-
}
|
|
10436
|
-
return { success: false, error: fileTypeResult.error };
|
|
10437
|
-
}
|
|
10438
|
-
const fileType = fileTypeResult;
|
|
10439
|
-
const sourceResult = prepareSourcePath(filePath, stdinContent, fileType);
|
|
10440
|
-
if ("error" in sourceResult) {
|
|
10441
|
-
const item = await library.find(identifier, { idType });
|
|
10442
|
-
if (!item) {
|
|
10443
|
-
return { success: false, error: `Reference '${identifier}' not found` };
|
|
10444
|
-
}
|
|
10445
|
-
return { success: false, error: sourceResult.error };
|
|
10446
|
-
}
|
|
10447
|
-
const { sourcePath, tempDir } = sourceResult;
|
|
10448
|
-
try {
|
|
10449
|
-
const result = await addAttachment(library, {
|
|
10450
|
-
identifier,
|
|
10451
|
-
filePath: sourcePath,
|
|
10452
|
-
role: FULLTEXT_ROLE,
|
|
10453
|
-
move: move ?? false,
|
|
10454
|
-
force: force ?? false,
|
|
10455
|
-
idType,
|
|
10456
|
-
attachmentsDirectory: fulltextDirectory
|
|
10457
|
-
});
|
|
10458
|
-
await cleanupTempDir(tempDir);
|
|
10459
|
-
return convertResult(result, fileType);
|
|
10460
|
-
} catch (error) {
|
|
10461
|
-
await cleanupTempDir(tempDir);
|
|
10462
|
-
throw error;
|
|
10463
|
-
}
|
|
10464
|
-
}
|
|
10465
|
-
function buildFilePath$1(attachmentsDirectory, directory, filename) {
|
|
10466
|
-
return normalizePathForOutput(join(attachmentsDirectory, directory, filename));
|
|
10467
|
-
}
|
|
10468
|
-
async function getFileContent(filePath) {
|
|
10469
|
-
const content = await readFile(filePath);
|
|
10470
|
-
return { success: true, content };
|
|
10471
|
-
}
|
|
10472
|
-
async function handleStdoutMode(attachments, type2, identifier, fulltextDirectory) {
|
|
10473
|
-
const file = findFulltextFile(attachments, type2);
|
|
10474
|
-
if (!file || !attachments?.directory) {
|
|
10475
|
-
return { success: false, error: `No ${type2} fulltext attached to '${identifier}'` };
|
|
10476
|
-
}
|
|
10477
|
-
const filePath = buildFilePath$1(fulltextDirectory, attachments.directory, file.filename);
|
|
10478
|
-
try {
|
|
10479
|
-
return await getFileContent(filePath);
|
|
10480
|
-
} catch {
|
|
10481
|
-
return { success: false, error: `No ${type2} fulltext attached to '${identifier}'` };
|
|
10482
|
-
}
|
|
10483
|
-
}
|
|
10484
|
-
function getSingleTypePath(attachments, type2, identifier, fulltextDirectory) {
|
|
10485
|
-
const file = findFulltextFile(attachments, type2);
|
|
10486
|
-
if (!file || !attachments?.directory) {
|
|
10487
|
-
return { success: false, error: `No ${type2} fulltext attached to '${identifier}'` };
|
|
10488
|
-
}
|
|
10489
|
-
const filePath = buildFilePath$1(fulltextDirectory, attachments.directory, file.filename);
|
|
10490
|
-
const paths = {};
|
|
10491
|
-
paths[type2] = filePath;
|
|
10492
|
-
return { success: true, paths };
|
|
10493
|
-
}
|
|
10494
|
-
function getAllFulltextPaths(attachments, fulltextFiles, fulltextDirectory, identifier) {
|
|
10495
|
-
const paths = {};
|
|
10496
|
-
for (const file of fulltextFiles) {
|
|
10497
|
-
const ext = file.filename.split(".").pop() || "";
|
|
10498
|
-
const format2 = extensionToFormat(ext);
|
|
10499
|
-
if (format2) {
|
|
10500
|
-
const filePath = buildFilePath$1(fulltextDirectory, attachments.directory, file.filename);
|
|
10501
|
-
paths[format2] = filePath;
|
|
10502
|
-
}
|
|
10503
|
-
}
|
|
10504
|
-
if (Object.keys(paths).length === 0) {
|
|
10505
|
-
return { success: false, error: `No fulltext attached to '${identifier}'` };
|
|
10506
|
-
}
|
|
10507
|
-
return { success: true, paths };
|
|
10508
|
-
}
|
|
10509
|
-
async function fulltextGet(library, options) {
|
|
10510
|
-
const { identifier, type: type2, stdout: stdout2, idType = "id", fulltextDirectory } = options;
|
|
10511
|
-
const item = await library.find(identifier, { idType });
|
|
10512
|
-
if (!item) {
|
|
10513
|
-
return { success: false, error: `Reference '${identifier}' not found` };
|
|
10514
|
-
}
|
|
10515
|
-
const attachments = item.custom?.attachments;
|
|
10516
|
-
if (stdout2 && type2) {
|
|
10517
|
-
return handleStdoutMode(attachments, type2, identifier, fulltextDirectory);
|
|
10518
|
-
}
|
|
10519
|
-
const fulltextFiles = findFulltextFiles(attachments);
|
|
10520
|
-
if (fulltextFiles.length === 0) {
|
|
10521
|
-
return { success: false, error: `No fulltext attached to '${identifier}'` };
|
|
10522
|
-
}
|
|
10523
|
-
if (type2) {
|
|
10524
|
-
return getSingleTypePath(attachments, type2, identifier, fulltextDirectory);
|
|
10525
|
-
}
|
|
10526
|
-
if (!attachments) {
|
|
10527
|
-
return { success: false, error: `No fulltext attached to '${identifier}'` };
|
|
10528
|
-
}
|
|
10529
|
-
return getAllFulltextPaths(attachments, fulltextFiles, fulltextDirectory, identifier);
|
|
10530
|
-
}
|
|
10531
10185
|
function getFilesToDetach(attachments, type2) {
|
|
10532
10186
|
if (type2) {
|
|
10533
10187
|
const file = findFulltextFile(attachments, type2);
|
|
@@ -10684,6 +10338,33 @@ async function executeFulltextOpen(options, context) {
|
|
|
10684
10338
|
};
|
|
10685
10339
|
return fulltextOpen(context.library, operationOptions);
|
|
10686
10340
|
}
|
|
10341
|
+
async function executeFulltextDiscover(options, context, config2) {
|
|
10342
|
+
const operationOptions = {
|
|
10343
|
+
identifier: options.identifier,
|
|
10344
|
+
idType: options.idType,
|
|
10345
|
+
fulltextConfig: config2.fulltext
|
|
10346
|
+
};
|
|
10347
|
+
return fulltextDiscover(context.library, operationOptions);
|
|
10348
|
+
}
|
|
10349
|
+
async function executeFulltextFetch(options, context, config2) {
|
|
10350
|
+
const operationOptions = {
|
|
10351
|
+
identifier: options.identifier,
|
|
10352
|
+
idType: options.idType,
|
|
10353
|
+
fulltextConfig: config2.fulltext,
|
|
10354
|
+
fulltextDirectory: options.fulltextDirectory,
|
|
10355
|
+
source: options.source,
|
|
10356
|
+
force: options.force
|
|
10357
|
+
};
|
|
10358
|
+
return fulltextFetch(context.library, operationOptions);
|
|
10359
|
+
}
|
|
10360
|
+
async function executeFulltextConvert(options, context) {
|
|
10361
|
+
const operationOptions = {
|
|
10362
|
+
identifier: options.identifier,
|
|
10363
|
+
idType: options.idType,
|
|
10364
|
+
fulltextDirectory: options.fulltextDirectory
|
|
10365
|
+
};
|
|
10366
|
+
return fulltextConvert(context.library, operationOptions);
|
|
10367
|
+
}
|
|
10687
10368
|
function formatFulltextAttachOutput(result) {
|
|
10688
10369
|
if (result.requiresConfirmation) {
|
|
10689
10370
|
return `File already attached: ${result.existingFile}
|
|
@@ -10736,12 +10417,50 @@ function formatFulltextOpenOutput(result) {
|
|
|
10736
10417
|
}
|
|
10737
10418
|
return `Opened ${result.openedType}: ${result.openedPath}`;
|
|
10738
10419
|
}
|
|
10420
|
+
function formatFulltextDiscoverOutput(result, identifier) {
|
|
10421
|
+
if (!result.success) {
|
|
10422
|
+
return `Error: ${result.error}`;
|
|
10423
|
+
}
|
|
10424
|
+
if (!result.locations || result.locations.length === 0) {
|
|
10425
|
+
return `No OA sources found for ${identifier}`;
|
|
10426
|
+
}
|
|
10427
|
+
const lines = [`OA sources for ${identifier}:`];
|
|
10428
|
+
for (const loc of result.locations) {
|
|
10429
|
+
const license = loc.license ? ` (${loc.license})` : "";
|
|
10430
|
+
lines.push(` ${loc.source}: ${loc.url}${license}`);
|
|
10431
|
+
}
|
|
10432
|
+
if (result.errors && result.errors.length > 0) {
|
|
10433
|
+
for (const err of result.errors) {
|
|
10434
|
+
lines.push(` Warning: ${err.source}: ${err.error}`);
|
|
10435
|
+
}
|
|
10436
|
+
}
|
|
10437
|
+
return lines.join("\n");
|
|
10438
|
+
}
|
|
10439
|
+
function formatFulltextFetchOutput(result) {
|
|
10440
|
+
if (!result.success) {
|
|
10441
|
+
return `Error: ${result.error}`;
|
|
10442
|
+
}
|
|
10443
|
+
const lines = [];
|
|
10444
|
+
if (result.source) {
|
|
10445
|
+
lines.push(`Source: ${result.source}`);
|
|
10446
|
+
}
|
|
10447
|
+
for (const file of result.attachedFiles ?? []) {
|
|
10448
|
+
lines.push(`Attached ${file}: fulltext.${file === "markdown" ? "md" : file}`);
|
|
10449
|
+
}
|
|
10450
|
+
return lines.join("\n");
|
|
10451
|
+
}
|
|
10452
|
+
function formatFulltextConvertOutput(result) {
|
|
10453
|
+
if (!result.success) {
|
|
10454
|
+
return `Error: ${result.error}`;
|
|
10455
|
+
}
|
|
10456
|
+
return `Converted PMC XML to Markdown: ${result.filename}`;
|
|
10457
|
+
}
|
|
10739
10458
|
function getFulltextExitCode(result) {
|
|
10740
10459
|
return result.success ? 0 : 1;
|
|
10741
10460
|
}
|
|
10742
10461
|
async function executeInteractiveSelect$1(context, config2) {
|
|
10743
10462
|
const { withAlternateScreen: withAlternateScreen2 } = await Promise.resolve().then(() => alternateScreen);
|
|
10744
|
-
const { selectReferencesOrExit } = await import("./reference-select-
|
|
10463
|
+
const { selectReferencesOrExit } = await import("./reference-select-DTqhevya.js");
|
|
10745
10464
|
const allReferences = await context.library.getAll();
|
|
10746
10465
|
const identifiers = await withAlternateScreen2(
|
|
10747
10466
|
() => selectReferencesOrExit(allReferences, { multiSelect: false }, config2.cli.tui)
|
|
@@ -10932,22 +10651,167 @@ async function handleFulltextOpenAction(identifierArg, options, globalOpts) {
|
|
|
10932
10651
|
setExitCode(ExitCode.INTERNAL_ERROR);
|
|
10933
10652
|
}
|
|
10934
10653
|
}
|
|
10654
|
+
async function handleFulltextDiscoverAction(identifierArg, options, globalOpts) {
|
|
10655
|
+
try {
|
|
10656
|
+
const config2 = await loadConfigWithOverrides({ ...globalOpts, ...options });
|
|
10657
|
+
const context = await createExecutionContext(config2, Library.load);
|
|
10658
|
+
let identifier;
|
|
10659
|
+
if (identifierArg) {
|
|
10660
|
+
identifier = identifierArg;
|
|
10661
|
+
} else if (isTTY()) {
|
|
10662
|
+
identifier = await executeInteractiveSelect$1(context, config2);
|
|
10663
|
+
} else {
|
|
10664
|
+
const stdinId = await readIdentifierFromStdin();
|
|
10665
|
+
if (!stdinId) {
|
|
10666
|
+
process.stderr.write(
|
|
10667
|
+
"Error: No identifier provided. Provide an ID, pipe one via stdin, or run interactively in a TTY.\n"
|
|
10668
|
+
);
|
|
10669
|
+
setExitCode(ExitCode.ERROR);
|
|
10670
|
+
return;
|
|
10671
|
+
}
|
|
10672
|
+
identifier = stdinId;
|
|
10673
|
+
}
|
|
10674
|
+
const discoverOptions = {
|
|
10675
|
+
identifier,
|
|
10676
|
+
...options.uuid && { idType: "uuid" }
|
|
10677
|
+
};
|
|
10678
|
+
const result = await executeFulltextDiscover(discoverOptions, context, config2);
|
|
10679
|
+
const output = formatFulltextDiscoverOutput(result, identifier);
|
|
10680
|
+
if (result.success) {
|
|
10681
|
+
process.stdout.write(`${output}
|
|
10682
|
+
`);
|
|
10683
|
+
} else {
|
|
10684
|
+
process.stderr.write(`${output}
|
|
10685
|
+
`);
|
|
10686
|
+
}
|
|
10687
|
+
setExitCode(getFulltextExitCode(result));
|
|
10688
|
+
} catch (error) {
|
|
10689
|
+
process.stderr.write(`Error: ${error instanceof Error ? error.message : String(error)}
|
|
10690
|
+
`);
|
|
10691
|
+
setExitCode(ExitCode.INTERNAL_ERROR);
|
|
10692
|
+
}
|
|
10693
|
+
}
|
|
10694
|
+
async function handleFulltextFetchAction(identifierArg, options, globalOpts) {
|
|
10695
|
+
try {
|
|
10696
|
+
const config2 = await loadConfigWithOverrides({ ...globalOpts, ...options });
|
|
10697
|
+
const context = await createExecutionContext(config2, Library.load);
|
|
10698
|
+
let identifier;
|
|
10699
|
+
if (identifierArg) {
|
|
10700
|
+
identifier = identifierArg;
|
|
10701
|
+
} else if (isTTY()) {
|
|
10702
|
+
identifier = await executeInteractiveSelect$1(context, config2);
|
|
10703
|
+
} else {
|
|
10704
|
+
const stdinId = await readIdentifierFromStdin();
|
|
10705
|
+
if (!stdinId) {
|
|
10706
|
+
process.stderr.write(
|
|
10707
|
+
"Error: No identifier provided. Provide an ID, pipe one via stdin, or run interactively in a TTY.\n"
|
|
10708
|
+
);
|
|
10709
|
+
setExitCode(ExitCode.ERROR);
|
|
10710
|
+
return;
|
|
10711
|
+
}
|
|
10712
|
+
identifier = stdinId;
|
|
10713
|
+
}
|
|
10714
|
+
process.stderr.write(`Fetching fulltext for ${identifier}...
|
|
10715
|
+
`);
|
|
10716
|
+
const fetchOptions = {
|
|
10717
|
+
identifier,
|
|
10718
|
+
fulltextDirectory: config2.attachments.directory,
|
|
10719
|
+
...options.source && { source: options.source },
|
|
10720
|
+
...options.force && { force: options.force },
|
|
10721
|
+
...options.uuid && { idType: "uuid" }
|
|
10722
|
+
};
|
|
10723
|
+
const result = await executeFulltextFetch(fetchOptions, context, config2);
|
|
10724
|
+
const output = formatFulltextFetchOutput(result);
|
|
10725
|
+
process.stderr.write(`${output}
|
|
10726
|
+
`);
|
|
10727
|
+
setExitCode(getFulltextExitCode(result));
|
|
10728
|
+
} catch (error) {
|
|
10729
|
+
process.stderr.write(`Error: ${error instanceof Error ? error.message : String(error)}
|
|
10730
|
+
`);
|
|
10731
|
+
setExitCode(ExitCode.INTERNAL_ERROR);
|
|
10732
|
+
}
|
|
10733
|
+
}
|
|
10734
|
+
async function handleFulltextConvertAction(identifierArg, options, globalOpts) {
|
|
10735
|
+
try {
|
|
10736
|
+
const config2 = await loadConfigWithOverrides({ ...globalOpts, ...options });
|
|
10737
|
+
const context = await createExecutionContext(config2, Library.load);
|
|
10738
|
+
let identifier;
|
|
10739
|
+
if (identifierArg) {
|
|
10740
|
+
identifier = identifierArg;
|
|
10741
|
+
} else if (isTTY()) {
|
|
10742
|
+
identifier = await executeInteractiveSelect$1(context, config2);
|
|
10743
|
+
} else {
|
|
10744
|
+
const stdinId = await readIdentifierFromStdin();
|
|
10745
|
+
if (!stdinId) {
|
|
10746
|
+
process.stderr.write(
|
|
10747
|
+
"Error: No identifier provided. Provide an ID, pipe one via stdin, or run interactively in a TTY.\n"
|
|
10748
|
+
);
|
|
10749
|
+
setExitCode(ExitCode.ERROR);
|
|
10750
|
+
return;
|
|
10751
|
+
}
|
|
10752
|
+
identifier = stdinId;
|
|
10753
|
+
}
|
|
10754
|
+
const convertOptions = {
|
|
10755
|
+
identifier,
|
|
10756
|
+
fulltextDirectory: config2.attachments.directory,
|
|
10757
|
+
...options.uuid && { idType: "uuid" }
|
|
10758
|
+
};
|
|
10759
|
+
const result = await executeFulltextConvert(convertOptions, context);
|
|
10760
|
+
const output = formatFulltextConvertOutput(result);
|
|
10761
|
+
process.stderr.write(`${output}
|
|
10762
|
+
`);
|
|
10763
|
+
setExitCode(getFulltextExitCode(result));
|
|
10764
|
+
} catch (error) {
|
|
10765
|
+
process.stderr.write(`Error: ${error instanceof Error ? error.message : String(error)}
|
|
10766
|
+
`);
|
|
10767
|
+
setExitCode(ExitCode.INTERNAL_ERROR);
|
|
10768
|
+
}
|
|
10769
|
+
}
|
|
10935
10770
|
const fulltext = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
10936
10771
|
__proto__: null,
|
|
10937
10772
|
executeFulltextAttach,
|
|
10773
|
+
executeFulltextConvert,
|
|
10938
10774
|
executeFulltextDetach,
|
|
10775
|
+
executeFulltextDiscover,
|
|
10776
|
+
executeFulltextFetch,
|
|
10939
10777
|
executeFulltextGet,
|
|
10940
10778
|
executeFulltextOpen,
|
|
10941
10779
|
formatFulltextAttachOutput,
|
|
10780
|
+
formatFulltextConvertOutput,
|
|
10942
10781
|
formatFulltextDetachOutput,
|
|
10782
|
+
formatFulltextDiscoverOutput,
|
|
10783
|
+
formatFulltextFetchOutput,
|
|
10943
10784
|
formatFulltextGetOutput,
|
|
10944
10785
|
formatFulltextOpenOutput,
|
|
10945
10786
|
getFulltextExitCode,
|
|
10946
10787
|
handleFulltextAttachAction,
|
|
10788
|
+
handleFulltextConvertAction,
|
|
10947
10789
|
handleFulltextDetachAction,
|
|
10790
|
+
handleFulltextDiscoverAction,
|
|
10791
|
+
handleFulltextFetchAction,
|
|
10948
10792
|
handleFulltextGetAction,
|
|
10949
10793
|
handleFulltextOpenAction
|
|
10950
10794
|
}, Symbol.toStringTag, { value: "Module" }));
|
|
10795
|
+
function buildResourceIndicators(item) {
|
|
10796
|
+
const icons = [];
|
|
10797
|
+
const attachments = item.custom?.attachments;
|
|
10798
|
+
const fulltextFiles = findFulltextFiles(attachments);
|
|
10799
|
+
const hasFulltextPdf = fulltextFiles.some(
|
|
10800
|
+
(f) => extensionToFormat(getExtension(f.filename)) === "pdf"
|
|
10801
|
+
);
|
|
10802
|
+
if (hasFulltextPdf) icons.push("📄");
|
|
10803
|
+
const hasFulltextMd = fulltextFiles.some(
|
|
10804
|
+
(f) => extensionToFormat(getExtension(f.filename)) === "markdown"
|
|
10805
|
+
);
|
|
10806
|
+
if (hasFulltextMd) icons.push("📝");
|
|
10807
|
+
const allFiles = attachments?.files ?? [];
|
|
10808
|
+
const hasOtherAttachments = allFiles.length > fulltextFiles.length;
|
|
10809
|
+
if (hasOtherAttachments) icons.push("📎");
|
|
10810
|
+
if (item.URL) icons.push("🔗");
|
|
10811
|
+
const tags = item.custom?.tags;
|
|
10812
|
+
if (Array.isArray(tags) && tags.length > 0) icons.push("🏷");
|
|
10813
|
+
return icons.join("");
|
|
10814
|
+
}
|
|
10951
10815
|
function formatAuthor(author) {
|
|
10952
10816
|
const family = author.family || "";
|
|
10953
10817
|
const givenInitial = author.given ? `${author.given.charAt(0)}.` : "";
|
|
@@ -10980,6 +10844,10 @@ function formatSingleReference(item) {
|
|
|
10980
10844
|
}
|
|
10981
10845
|
const uuid2 = item.custom?.uuid || "(no uuid)";
|
|
10982
10846
|
lines.push(` UUID: ${uuid2}`);
|
|
10847
|
+
const indicators = buildResourceIndicators(item);
|
|
10848
|
+
if (indicators) {
|
|
10849
|
+
lines.push(` ${indicators}`);
|
|
10850
|
+
}
|
|
10983
10851
|
return lines.join("\n");
|
|
10984
10852
|
}
|
|
10985
10853
|
function formatPretty(items2) {
|
|
@@ -31588,6 +31456,115 @@ function registerFulltextDetachTool(server, getLibraryOperations, getConfig) {
|
|
|
31588
31456
|
}
|
|
31589
31457
|
);
|
|
31590
31458
|
}
|
|
31459
|
+
function registerFulltextDiscoverTool(server, getLibraryOperations, getConfig) {
|
|
31460
|
+
server.registerTool(
|
|
31461
|
+
"fulltext_discover",
|
|
31462
|
+
{
|
|
31463
|
+
description: "Check Open Access (OA) availability for a reference. Returns available sources and URLs.",
|
|
31464
|
+
inputSchema: {
|
|
31465
|
+
id: z.string().describe("Reference ID")
|
|
31466
|
+
}
|
|
31467
|
+
},
|
|
31468
|
+
async (args) => {
|
|
31469
|
+
const libraryOps = getLibraryOperations();
|
|
31470
|
+
const config2 = getConfig();
|
|
31471
|
+
const result = await fulltextDiscover(libraryOps, {
|
|
31472
|
+
identifier: args.id,
|
|
31473
|
+
fulltextConfig: config2.fulltext
|
|
31474
|
+
});
|
|
31475
|
+
if (!result.success) {
|
|
31476
|
+
return {
|
|
31477
|
+
content: [{ type: "text", text: result.error ?? "Unknown error" }],
|
|
31478
|
+
isError: true
|
|
31479
|
+
};
|
|
31480
|
+
}
|
|
31481
|
+
if (!result.locations || result.locations.length === 0) {
|
|
31482
|
+
return {
|
|
31483
|
+
content: [{ type: "text", text: `No OA sources found for '${args.id}'` }]
|
|
31484
|
+
};
|
|
31485
|
+
}
|
|
31486
|
+
const lines = [`OA sources for '${args.id}' (status: ${result.oaStatus}):`];
|
|
31487
|
+
for (const loc of result.locations) {
|
|
31488
|
+
lines.push(` ${loc.source}: ${loc.url} (${loc.urlType})`);
|
|
31489
|
+
}
|
|
31490
|
+
return {
|
|
31491
|
+
content: [{ type: "text", text: lines.join("\n") }]
|
|
31492
|
+
};
|
|
31493
|
+
}
|
|
31494
|
+
);
|
|
31495
|
+
}
|
|
31496
|
+
function registerFulltextFetchTool(server, getLibraryOperations, getConfig) {
|
|
31497
|
+
server.registerTool(
|
|
31498
|
+
"fulltext_fetch",
|
|
31499
|
+
{
|
|
31500
|
+
description: "Download and attach Open Access full-text for a reference. Automatically discovers OA sources, downloads PDF/XML, converts PMC XML to Markdown, and attaches files.",
|
|
31501
|
+
inputSchema: {
|
|
31502
|
+
id: z.string().describe("Reference ID"),
|
|
31503
|
+
source: z.enum(["pmc", "arxiv", "unpaywall", "core"]).optional().describe("Preferred source (optional)"),
|
|
31504
|
+
force: z.boolean().optional().describe("Force overwrite existing attachment")
|
|
31505
|
+
}
|
|
31506
|
+
},
|
|
31507
|
+
async (args) => {
|
|
31508
|
+
const libraryOps = getLibraryOperations();
|
|
31509
|
+
const config2 = getConfig();
|
|
31510
|
+
const result = await fulltextFetch(libraryOps, {
|
|
31511
|
+
identifier: args.id,
|
|
31512
|
+
fulltextConfig: config2.fulltext,
|
|
31513
|
+
fulltextDirectory: config2.attachments.directory,
|
|
31514
|
+
source: args.source,
|
|
31515
|
+
force: args.force
|
|
31516
|
+
});
|
|
31517
|
+
if (!result.success) {
|
|
31518
|
+
return {
|
|
31519
|
+
content: [{ type: "text", text: result.error ?? "Unknown error" }],
|
|
31520
|
+
isError: true
|
|
31521
|
+
};
|
|
31522
|
+
}
|
|
31523
|
+
const files = result.attachedFiles?.join(", ") ?? "none";
|
|
31524
|
+
return {
|
|
31525
|
+
content: [
|
|
31526
|
+
{
|
|
31527
|
+
type: "text",
|
|
31528
|
+
text: `Fetched fulltext for '${args.id}' from ${result.source}: ${files}`
|
|
31529
|
+
}
|
|
31530
|
+
]
|
|
31531
|
+
};
|
|
31532
|
+
}
|
|
31533
|
+
);
|
|
31534
|
+
}
|
|
31535
|
+
function registerFulltextConvertTool(server, getLibraryOperations, getConfig) {
|
|
31536
|
+
server.registerTool(
|
|
31537
|
+
"fulltext_convert",
|
|
31538
|
+
{
|
|
31539
|
+
description: "Convert an attached PMC JATS XML file to Markdown for a reference.",
|
|
31540
|
+
inputSchema: {
|
|
31541
|
+
id: z.string().describe("Reference ID")
|
|
31542
|
+
}
|
|
31543
|
+
},
|
|
31544
|
+
async (args) => {
|
|
31545
|
+
const libraryOps = getLibraryOperations();
|
|
31546
|
+
const config2 = getConfig();
|
|
31547
|
+
const result = await fulltextConvert(libraryOps, {
|
|
31548
|
+
identifier: args.id,
|
|
31549
|
+
fulltextDirectory: config2.attachments.directory
|
|
31550
|
+
});
|
|
31551
|
+
if (!result.success) {
|
|
31552
|
+
return {
|
|
31553
|
+
content: [{ type: "text", text: result.error ?? "Unknown error" }],
|
|
31554
|
+
isError: true
|
|
31555
|
+
};
|
|
31556
|
+
}
|
|
31557
|
+
return {
|
|
31558
|
+
content: [
|
|
31559
|
+
{
|
|
31560
|
+
type: "text",
|
|
31561
|
+
text: `Converted PMC XML to Markdown for '${args.id}': ${result.filename}`
|
|
31562
|
+
}
|
|
31563
|
+
]
|
|
31564
|
+
};
|
|
31565
|
+
}
|
|
31566
|
+
);
|
|
31567
|
+
}
|
|
31591
31568
|
function registerListTool(server, getLibraryOperations, getConfig) {
|
|
31592
31569
|
server.registerTool(
|
|
31593
31570
|
"list",
|
|
@@ -31721,6 +31698,9 @@ function registerAllTools(server, getLibraryOperations, getConfig) {
|
|
|
31721
31698
|
registerFulltextAttachTool(server, getLibraryOperations, getConfig);
|
|
31722
31699
|
registerFulltextGetTool(server, getLibraryOperations, getConfig);
|
|
31723
31700
|
registerFulltextDetachTool(server, getLibraryOperations, getConfig);
|
|
31701
|
+
registerFulltextDiscoverTool(server, getLibraryOperations, getConfig);
|
|
31702
|
+
registerFulltextFetchTool(server, getLibraryOperations, getConfig);
|
|
31703
|
+
registerFulltextConvertTool(server, getLibraryOperations, getConfig);
|
|
31724
31704
|
}
|
|
31725
31705
|
async function createMcpServer(options) {
|
|
31726
31706
|
const serverInfo = {
|
|
@@ -31774,7 +31754,7 @@ async function mcpStart(options) {
|
|
|
31774
31754
|
async function executeRemove(options, context) {
|
|
31775
31755
|
const { identifier, idType = "id", fulltextDirectory, deleteFulltext = false } = options;
|
|
31776
31756
|
if (context.mode === "local" && deleteFulltext && fulltextDirectory) {
|
|
31777
|
-
const { removeReference } = await import("./index-
|
|
31757
|
+
const { removeReference } = await import("./index-BK3hTiKR.js").then((n) => n.v);
|
|
31778
31758
|
return removeReference(context.library, {
|
|
31779
31759
|
identifier,
|
|
31780
31760
|
idType,
|
|
@@ -31829,7 +31809,7 @@ Continue?`;
|
|
|
31829
31809
|
}
|
|
31830
31810
|
async function executeInteractiveRemove(context, config2) {
|
|
31831
31811
|
const { withAlternateScreen: withAlternateScreen2 } = await Promise.resolve().then(() => alternateScreen);
|
|
31832
|
-
const { selectReferenceItemsOrExit } = await import("./reference-select-
|
|
31812
|
+
const { selectReferenceItemsOrExit } = await import("./reference-select-DTqhevya.js");
|
|
31833
31813
|
const allReferences = await context.library.getAll();
|
|
31834
31814
|
const selectedItems = await withAlternateScreen2(
|
|
31835
31815
|
() => selectReferenceItemsOrExit(allReferences, { multiSelect: false }, config2.cli.tui)
|
|
@@ -32054,7 +32034,7 @@ async function executeInteractiveSearch(options, context, config2) {
|
|
|
32054
32034
|
validateInteractiveOptions(options);
|
|
32055
32035
|
const { checkTTY } = await import("./tty-BMyaEOhX.js");
|
|
32056
32036
|
const { withAlternateScreen: withAlternateScreen2 } = await Promise.resolve().then(() => alternateScreen);
|
|
32057
|
-
const { runSearchFlow } = await import("./index-
|
|
32037
|
+
const { runSearchFlow } = await import("./index-BvIfOciH.js");
|
|
32058
32038
|
const { search } = await import("./file-watcher-CrsNHUpz.js").then((n) => n.z);
|
|
32059
32039
|
const { tokenize } = await import("./file-watcher-CrsNHUpz.js").then((n) => n.y);
|
|
32060
32040
|
checkTTY();
|
|
@@ -32073,7 +32053,7 @@ async function executeInteractiveSearch(options, context, config2) {
|
|
|
32073
32053
|
})
|
|
32074
32054
|
);
|
|
32075
32055
|
if (result.selectedItems && !result.cancelled) {
|
|
32076
|
-
const { isSideEffectAction } = await import("./action-menu-
|
|
32056
|
+
const { isSideEffectAction } = await import("./action-menu-B1DCdkkp.js");
|
|
32077
32057
|
if (isSideEffectAction(result.action)) {
|
|
32078
32058
|
await executeSideEffectAction(result.action, result.selectedItems, context, config2);
|
|
32079
32059
|
return { output: "", cancelled: false, action: result.action };
|
|
@@ -32089,7 +32069,7 @@ async function executeSideEffectAction(action, items2, context, config2) {
|
|
|
32089
32069
|
switch (action) {
|
|
32090
32070
|
case "open-url": {
|
|
32091
32071
|
const { resolveDefaultUrl: resolveDefaultUrl2 } = await Promise.resolve().then(() => url);
|
|
32092
|
-
const { openWithSystemApp: openWithSystemApp2 } = await import("./loader-
|
|
32072
|
+
const { openWithSystemApp: openWithSystemApp2 } = await import("./loader-BHvcats5.js").then((n) => n.j);
|
|
32093
32073
|
const item = items2[0];
|
|
32094
32074
|
if (!item) return;
|
|
32095
32075
|
const url$1 = resolveDefaultUrl2(item);
|
|
@@ -32476,7 +32456,7 @@ function formatUpdateOutput(result, identifier) {
|
|
|
32476
32456
|
}
|
|
32477
32457
|
async function executeInteractiveUpdate(context, config2) {
|
|
32478
32458
|
const { withAlternateScreen: withAlternateScreen2 } = await Promise.resolve().then(() => alternateScreen);
|
|
32479
|
-
const { selectReferencesOrExit } = await import("./reference-select-
|
|
32459
|
+
const { selectReferencesOrExit } = await import("./reference-select-DTqhevya.js");
|
|
32480
32460
|
const allReferences = await context.library.getAll();
|
|
32481
32461
|
const identifiers = await withAlternateScreen2(
|
|
32482
32462
|
() => selectReferencesOrExit(allReferences, { multiSelect: false }, config2.cli.tui)
|
|
@@ -32771,7 +32751,7 @@ function getUrlExitCode(result) {
|
|
|
32771
32751
|
}
|
|
32772
32752
|
async function executeInteractiveSelect(context, config2) {
|
|
32773
32753
|
const { withAlternateScreen: withAlternateScreen2 } = await Promise.resolve().then(() => alternateScreen);
|
|
32774
|
-
const { selectReferencesOrExit } = await import("./reference-select-
|
|
32754
|
+
const { selectReferencesOrExit } = await import("./reference-select-DTqhevya.js");
|
|
32775
32755
|
const allReferences = await context.library.getAll();
|
|
32776
32756
|
const identifiers = await withAlternateScreen2(
|
|
32777
32757
|
() => selectReferencesOrExit(allReferences, { multiSelect: false }, config2.cli.tui)
|
|
@@ -33267,6 +33247,35 @@ async function outputAddResultJson(result, context, full) {
|
|
|
33267
33247
|
process.stdout.write(`${JSON.stringify(jsonOutput2)}
|
|
33268
33248
|
`);
|
|
33269
33249
|
}
|
|
33250
|
+
function shouldAutoFetch(cliFlag, configEnabled) {
|
|
33251
|
+
if (cliFlag !== void 0) {
|
|
33252
|
+
return cliFlag;
|
|
33253
|
+
}
|
|
33254
|
+
return configEnabled;
|
|
33255
|
+
}
|
|
33256
|
+
async function performAutoFetch(addedItems, context, config2) {
|
|
33257
|
+
const { fulltextFetch: fulltextFetch2 } = await import("./index-BK3hTiKR.js").then((n) => n.u);
|
|
33258
|
+
const fetchResults = await autoFetchFulltext(addedItems, context, {
|
|
33259
|
+
fulltextConfig: config2.fulltext,
|
|
33260
|
+
fulltextDirectory: config2.attachments.directory,
|
|
33261
|
+
fulltextFetchFn: fulltextFetch2
|
|
33262
|
+
});
|
|
33263
|
+
for (const [i, fetchResult] of fetchResults.entries()) {
|
|
33264
|
+
const addedItem = addedItems[i];
|
|
33265
|
+
if (!addedItem) continue;
|
|
33266
|
+
if (fetchResult.success) {
|
|
33267
|
+
process.stderr.write(
|
|
33268
|
+
`Fulltext for ${addedItem.id}: ${formatFulltextFetchOutput(fetchResult)}
|
|
33269
|
+
`
|
|
33270
|
+
);
|
|
33271
|
+
} else {
|
|
33272
|
+
process.stderr.write(
|
|
33273
|
+
`Fulltext for ${addedItem.id}: ${fetchResult.error ?? "unknown error"}
|
|
33274
|
+
`
|
|
33275
|
+
);
|
|
33276
|
+
}
|
|
33277
|
+
}
|
|
33278
|
+
}
|
|
33270
33279
|
async function handleAddAction(inputs, options, program) {
|
|
33271
33280
|
const outputFormat = options.output ?? "text";
|
|
33272
33281
|
try {
|
|
@@ -33286,6 +33295,9 @@ async function handleAddAction(inputs, options, program) {
|
|
|
33286
33295
|
process.stderr.write(`${output}
|
|
33287
33296
|
`);
|
|
33288
33297
|
}
|
|
33298
|
+
if (result.added.length > 0 && shouldAutoFetch(options.fetchFulltext, config2.fulltext.autoFetchOnAdd)) {
|
|
33299
|
+
await performAutoFetch(result.added, context, config2);
|
|
33300
|
+
}
|
|
33289
33301
|
setExitCode(getExitCode(result));
|
|
33290
33302
|
} catch (error) {
|
|
33291
33303
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -33300,7 +33312,7 @@ async function handleAddAction(inputs, options, program) {
|
|
|
33300
33312
|
}
|
|
33301
33313
|
}
|
|
33302
33314
|
function registerAddCommand(program) {
|
|
33303
|
-
program.command("add").description("Add new reference(s) to the library").argument("[input...]", "File paths or identifiers (PMID/DOI/ISBN), or use stdin").option("-f, --force", "Skip duplicate detection").option("-i, --input <format>", "Input format: json|bibtex|ris|pmid|doi|isbn|auto", "auto").option("--verbose", "Show detailed error information").option("-o, --output <format>", "Output format: json|text", "text").option("--full", "Include full CSL-JSON data in JSON output").action(async (inputs, options) => {
|
|
33315
|
+
program.command("add").description("Add new reference(s) to the library").argument("[input...]", "File paths or identifiers (PMID/DOI/ISBN), or use stdin").option("-f, --force", "Skip duplicate detection").option("-i, --input <format>", "Input format: json|bibtex|ris|pmid|doi|isbn|auto", "auto").option("--verbose", "Show detailed error information").option("-o, --output <format>", "Output format: json|text", "text").option("--full", "Include full CSL-JSON data in JSON output").option("--fetch-fulltext", "Auto-fetch OA fulltext after adding").option("--no-fetch-fulltext", "Disable auto-fetch fulltext (overrides config)").action(async (inputs, options) => {
|
|
33304
33316
|
await handleAddAction(inputs, options, program);
|
|
33305
33317
|
});
|
|
33306
33318
|
}
|
|
@@ -33472,6 +33484,15 @@ function registerFulltextCommand(program) {
|
|
|
33472
33484
|
fulltextCmd.command("open").description("Open full-text file with system default application").argument("[identifier]", "Citation key or UUID (interactive selection if omitted)").option("--pdf", "Open PDF file").option("--markdown", "Open Markdown file").option("--uuid", "Interpret identifier as UUID").action(async (identifier, options) => {
|
|
33473
33485
|
await handleFulltextOpenAction(identifier, options, program.opts());
|
|
33474
33486
|
});
|
|
33487
|
+
fulltextCmd.command("discover").description("Check OA (Open Access) availability for a reference").argument("[identifier]", "Citation key or UUID (interactive selection if omitted)").option("--uuid", "Interpret identifier as UUID").action(async (identifier, options) => {
|
|
33488
|
+
await handleFulltextDiscoverAction(identifier, options, program.opts());
|
|
33489
|
+
});
|
|
33490
|
+
fulltextCmd.command("fetch").description("Download OA full text and auto-attach to a reference").argument("[identifier]", "Citation key or UUID (interactive selection if omitted)").option("--source <source>", "Preferred source: pmc, arxiv, unpaywall, core").option("-f, --force", "Overwrite existing fulltext attachment").option("--uuid", "Interpret identifier as UUID").action(async (identifier, options) => {
|
|
33491
|
+
await handleFulltextFetchAction(identifier, options, program.opts());
|
|
33492
|
+
});
|
|
33493
|
+
fulltextCmd.command("convert").description("Convert attached PMC XML to Markdown").argument("[identifier]", "Citation key or UUID (interactive selection if omitted)").option("--uuid", "Interpret identifier as UUID").action(async (identifier, options) => {
|
|
33494
|
+
await handleFulltextConvertAction(identifier, options, program.opts());
|
|
33495
|
+
});
|
|
33475
33496
|
}
|
|
33476
33497
|
function registerUrlCommand(program) {
|
|
33477
33498
|
program.command("url").description("Show URLs for references").argument("[identifiers...]", "Reference identifiers").option("--default", "Show only the best URL by priority").option("--doi", "Show only DOI URL").option("--pubmed", "Show only PubMed URL").option("--pmcid", "Show only PMC URL").option("--open", "Open URL in browser").option("--uuid", "Interpret identifiers as UUIDs").action(async (identifiers, options) => {
|
|
@@ -33495,8 +33516,8 @@ async function main(argv) {
|
|
|
33495
33516
|
}
|
|
33496
33517
|
export {
|
|
33497
33518
|
Select as S,
|
|
33498
|
-
|
|
33499
|
-
|
|
33519
|
+
stringify as a,
|
|
33520
|
+
buildResourceIndicators as b,
|
|
33500
33521
|
createProgram as c,
|
|
33501
33522
|
detachAttachment as d,
|
|
33502
33523
|
formatBibtex as f,
|
|
@@ -33508,4 +33529,4 @@ export {
|
|
|
33508
33529
|
restoreStdinAfterInk as r,
|
|
33509
33530
|
syncAttachments as s
|
|
33510
33531
|
};
|
|
33511
|
-
//# sourceMappingURL=index-
|
|
33532
|
+
//# sourceMappingURL=index-DOvEusHb.js.map
|