citeclaw 2.0.2 → 2.0.4
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/package.json +1 -1
- package/scripts/botcite.js +126 -15
package/package.json
CHANGED
package/scripts/botcite.js
CHANGED
|
@@ -28,13 +28,45 @@ const vendoredOfficialStylesDir = path.join( rootDir, 'vendor', 'styles-official
|
|
|
28
28
|
const localDir = path.join( rootDir, '.local' );
|
|
29
29
|
const logDir = path.join( localDir, 'logs' );
|
|
30
30
|
const stateDir = path.join( localDir, 'state' );
|
|
31
|
+
const localTranslatorSourcesDir = path.join( localDir, 'translator-sources' );
|
|
31
32
|
const mergedTranslatorsDir = process.env.LOCAL_TRANSLATORS_DIR ||
|
|
32
33
|
path.join( localDir, 'translators' );
|
|
33
34
|
const stylesRootDir = process.env.LOCAL_STYLES_DIR ||
|
|
34
35
|
path.join( localDir, 'styles' );
|
|
36
|
+
const localStyleSourcesDir = path.join( localDir, 'style-sources' );
|
|
35
37
|
const cslDir = path.join( stylesRootDir, 'csl' );
|
|
36
38
|
const localeDir = path.join( stylesRootDir, 'locales' );
|
|
37
39
|
const defaultStyleSources = [ vendoredOfficialStylesDir, vendoredStylesDir ];
|
|
40
|
+
const defaultStyleGitSources = [
|
|
41
|
+
{
|
|
42
|
+
url: 'https://github.com/citation-style-language/styles.git',
|
|
43
|
+
dir: path.join( localStyleSourcesDir, 'styles-official' )
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
url: 'https://github.com/zotero-chinese/styles.git',
|
|
47
|
+
dir: path.join( localStyleSourcesDir, 'styles' )
|
|
48
|
+
}
|
|
49
|
+
];
|
|
50
|
+
const defaultTranslatorGitSources = [
|
|
51
|
+
{
|
|
52
|
+
url: 'https://github.com/zotero/translators.git',
|
|
53
|
+
dir: path.join( localTranslatorSourcesDir, 'translators-official' )
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
url: 'https://github.com/l0o0/translators_CN.git',
|
|
57
|
+
dir: path.join( localTranslatorSourcesDir, 'translators_CN' )
|
|
58
|
+
}
|
|
59
|
+
];
|
|
60
|
+
|
|
61
|
+
function resolvedOfficialTranslatorsDir() {
|
|
62
|
+
const fallback = defaultTranslatorGitSources[ 0 ].dir;
|
|
63
|
+
return fileExists( officialTranslatorsDir ) ? officialTranslatorsDir : fallback;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function resolvedCnTranslatorsDir() {
|
|
67
|
+
const fallback = defaultTranslatorGitSources[ 1 ].dir;
|
|
68
|
+
return fileExists( cnTranslatorsDir ) ? cnTranslatorsDir : fallback;
|
|
69
|
+
}
|
|
38
70
|
const defaultPdfFetchIntervalMs = parseInt( process.env.CITOID_LOCAL_FETCH_INTERVAL_MS || '800', 10 );
|
|
39
71
|
const defaultRequestTimeoutMs = parseInt( process.env.CITOID_LOCAL_FETCH_TIMEOUT_MS || '15000', 10 );
|
|
40
72
|
const defaultProbeBodyBytes = parseInt( process.env.CITOID_LOCAL_PROBE_BODY_BYTES || '1572864', 10 );
|
|
@@ -80,6 +112,7 @@ function usage() {
|
|
|
80
112
|
console.error( ' citeclaw cite-pdf [--headers] [--debug-pdf] <pdf-path>' );
|
|
81
113
|
console.error( ' citeclaw fetch-pdf [--base <openurl-base>] [--out <file.pdf>] <doi|arxiv|url>' );
|
|
82
114
|
console.error( ' citeclaw openurl-resolve [--base <openurl-base>] <doi|arxiv|url>' );
|
|
115
|
+
console.error( ' citeclaw translators sync' );
|
|
83
116
|
console.error( ' citeclaw zotero <login|logout|whoami|query|dump|cite|add|delete|update|note|sync-cite|dedup|enrich|export|watch|templates|safe-mode> [...]' );
|
|
84
117
|
console.error( ' citeclaw batch --op <cite|cite-style|fetch-pdf|openurl-resolve> --in <file>' );
|
|
85
118
|
console.error( ' citeclaw styles sync [--repo <git-url>]' );
|
|
@@ -106,6 +139,7 @@ function usage() {
|
|
|
106
139
|
console.error( " citeclaw zotero query 'transformer'" );
|
|
107
140
|
console.error( ' citeclaw zotero cite AB12CD34' );
|
|
108
141
|
console.error( ' citeclaw batch --op cite --format bibtex --in ./ids.txt --out-jsonl ./result.jsonl' );
|
|
142
|
+
console.error( ' citeclaw translators sync' );
|
|
109
143
|
console.error( 'options:' );
|
|
110
144
|
console.error( ' --concurrency <n> batch worker count (default: 4)' );
|
|
111
145
|
console.error( ' --user-id <id> Zotero user id (or set ZOTERO_USER_ID)' );
|
|
@@ -469,13 +503,15 @@ function repoHeadOrMissing( repoPath ) {
|
|
|
469
503
|
|
|
470
504
|
function translatorsNeedSync() {
|
|
471
505
|
const stampPath = path.join( stateDir, 'translators-sync.stamp' );
|
|
506
|
+
const activeOfficialDir = resolvedOfficialTranslatorsDir();
|
|
507
|
+
const activeCnDir = resolvedCnTranslatorsDir();
|
|
472
508
|
const hasMerged = fileExists( mergedTranslatorsDir ) &&
|
|
473
509
|
fs.readdirSync( mergedTranslatorsDir ).some( ( name ) => name.endsWith( '.js' ) );
|
|
474
510
|
if ( !hasMerged ) {
|
|
475
511
|
return true;
|
|
476
512
|
}
|
|
477
513
|
|
|
478
|
-
const currentStamp = `zotero=${ repoHeadOrMissing( path.join( zoteroDir, 'modules', 'translators' ) ) };official=${ repoHeadOrMissing(
|
|
514
|
+
const currentStamp = `zotero=${ repoHeadOrMissing( path.join( zoteroDir, 'modules', 'translators' ) ) };official=${ repoHeadOrMissing( activeOfficialDir ) };cn=${ repoHeadOrMissing( activeCnDir ) }`;
|
|
479
515
|
let previousStamp = '';
|
|
480
516
|
if ( fileExists( stampPath ) ) {
|
|
481
517
|
previousStamp = fs.readFileSync( stampPath, 'utf8' ).trim();
|
|
@@ -485,7 +521,7 @@ function translatorsNeedSync() {
|
|
|
485
521
|
|
|
486
522
|
function writeTranslatorStamp() {
|
|
487
523
|
const stampPath = path.join( stateDir, 'translators-sync.stamp' );
|
|
488
|
-
const stamp = `zotero=${ repoHeadOrMissing( path.join( zoteroDir, 'modules', 'translators' ) ) };official=${ repoHeadOrMissing(
|
|
524
|
+
const stamp = `zotero=${ repoHeadOrMissing( path.join( zoteroDir, 'modules', 'translators' ) ) };official=${ repoHeadOrMissing( resolvedOfficialTranslatorsDir() ) };cn=${ repoHeadOrMissing( resolvedCnTranslatorsDir() ) }`;
|
|
489
525
|
fs.writeFileSync( stampPath, `${ stamp }\n` );
|
|
490
526
|
}
|
|
491
527
|
|
|
@@ -493,6 +529,8 @@ function syncMergedTranslators() {
|
|
|
493
529
|
fs.rmSync( mergedTranslatorsDir, { recursive: true, force: true } );
|
|
494
530
|
fs.mkdirSync( mergedTranslatorsDir, { recursive: true } );
|
|
495
531
|
const zoteroTranslators = path.join( zoteroDir, 'modules', 'translators' );
|
|
532
|
+
const activeOfficialDir = resolvedOfficialTranslatorsDir();
|
|
533
|
+
const activeCnDir = resolvedCnTranslatorsDir();
|
|
496
534
|
if ( fileExists( zoteroTranslators ) ) {
|
|
497
535
|
fs.readdirSync( zoteroTranslators )
|
|
498
536
|
.filter( ( name ) => name.endsWith( '.js' ) )
|
|
@@ -503,22 +541,22 @@ function syncMergedTranslators() {
|
|
|
503
541
|
);
|
|
504
542
|
} );
|
|
505
543
|
}
|
|
506
|
-
if ( fileExists(
|
|
507
|
-
fs.readdirSync(
|
|
544
|
+
if ( fileExists( activeOfficialDir ) ) {
|
|
545
|
+
fs.readdirSync( activeOfficialDir )
|
|
508
546
|
.filter( ( name ) => name.endsWith( '.js' ) )
|
|
509
547
|
.forEach( ( name ) => {
|
|
510
548
|
fs.copyFileSync(
|
|
511
|
-
path.join(
|
|
549
|
+
path.join( activeOfficialDir, name ),
|
|
512
550
|
path.join( mergedTranslatorsDir, name )
|
|
513
551
|
);
|
|
514
552
|
} );
|
|
515
553
|
}
|
|
516
|
-
if ( fileExists(
|
|
517
|
-
fs.readdirSync(
|
|
554
|
+
if ( fileExists( activeCnDir ) ) {
|
|
555
|
+
fs.readdirSync( activeCnDir )
|
|
518
556
|
.filter( ( name ) => name.endsWith( '.js' ) )
|
|
519
557
|
.forEach( ( name ) => {
|
|
520
558
|
fs.copyFileSync(
|
|
521
|
-
path.join(
|
|
559
|
+
path.join( activeCnDir, name ),
|
|
522
560
|
path.join( mergedTranslatorsDir, name )
|
|
523
561
|
);
|
|
524
562
|
} );
|
|
@@ -526,10 +564,51 @@ function syncMergedTranslators() {
|
|
|
526
564
|
writeTranslatorStamp();
|
|
527
565
|
}
|
|
528
566
|
|
|
567
|
+
function syncTranslators() {
|
|
568
|
+
ensureDirs();
|
|
569
|
+
const sources = ensureDefaultTranslatorSources();
|
|
570
|
+
syncMergedTranslators();
|
|
571
|
+
const mergedCount = fs.readdirSync( mergedTranslatorsDir )
|
|
572
|
+
.filter( ( name ) => name.endsWith( '.js' ) )
|
|
573
|
+
.length;
|
|
574
|
+
process.stdout.write(
|
|
575
|
+
`translators synced to ${ mergedTranslatorsDir } from ${ sources.officialDir }, ${ sources.cnDir } and ${ path.join( zoteroDir, 'modules', 'translators' ) } (${ mergedCount } files)\n`
|
|
576
|
+
);
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
function ensureDefaultTranslatorSources() {
|
|
580
|
+
const existingOfficial = fileExists( officialTranslatorsDir ) ? officialTranslatorsDir : null;
|
|
581
|
+
const existingCn = fileExists( cnTranslatorsDir ) ? cnTranslatorsDir : null;
|
|
582
|
+
if ( existingOfficial && existingCn ) {
|
|
583
|
+
return {
|
|
584
|
+
officialDir: existingOfficial,
|
|
585
|
+
cnDir: existingCn
|
|
586
|
+
};
|
|
587
|
+
}
|
|
588
|
+
if ( !commandExists( 'git' ) ) {
|
|
589
|
+
throw new Error(
|
|
590
|
+
'missing translator sources and git is not installed; install git or provide vendored translator directories'
|
|
591
|
+
);
|
|
592
|
+
}
|
|
593
|
+
fs.mkdirSync( localTranslatorSourcesDir, { recursive: true } );
|
|
594
|
+
defaultTranslatorGitSources.forEach( ( source ) => {
|
|
595
|
+
if ( fileExists( source.dir ) ) {
|
|
596
|
+
runCommandOrThrow( 'git', [ '-C', source.dir, 'pull', '--ff-only' ] );
|
|
597
|
+
return;
|
|
598
|
+
}
|
|
599
|
+
runCommandOrThrow( 'git', [ 'clone', '--depth', '1', source.url, source.dir ] );
|
|
600
|
+
} );
|
|
601
|
+
return {
|
|
602
|
+
officialDir: defaultTranslatorGitSources[ 0 ].dir,
|
|
603
|
+
cnDir: defaultTranslatorGitSources[ 1 ].dir
|
|
604
|
+
};
|
|
605
|
+
}
|
|
606
|
+
|
|
529
607
|
function bootstrapLocalEnvironment() {
|
|
530
608
|
ensureDirs();
|
|
531
609
|
const installer = resolveInstallCommand();
|
|
532
|
-
|
|
610
|
+
const translatorSources = ensureDefaultTranslatorSources();
|
|
611
|
+
if ( !repoReady( zoteroDir ) || !repoReady( translatorSources.cnDir ) || !repoReady( translatorSources.officialDir ) ) {
|
|
533
612
|
throw new Error(
|
|
534
613
|
'missing vendored repos under vendor/. pull the complete repository content.'
|
|
535
614
|
);
|
|
@@ -572,6 +651,27 @@ function ensureStyleRuntime() {
|
|
|
572
651
|
throw new Error( 'Styles are not synced. Run: citeclaw styles sync' );
|
|
573
652
|
}
|
|
574
653
|
|
|
654
|
+
function ensureDefaultStyleSources() {
|
|
655
|
+
const existing = defaultStyleSources.filter( ( sourceDir ) => fileExists( sourceDir ) );
|
|
656
|
+
if ( existing.length ) {
|
|
657
|
+
return existing;
|
|
658
|
+
}
|
|
659
|
+
if ( !commandExists( 'git' ) ) {
|
|
660
|
+
throw new Error(
|
|
661
|
+
'no bundled style sources found in this package, and git is not installed; install git or pass --repo <styles-dir>'
|
|
662
|
+
);
|
|
663
|
+
}
|
|
664
|
+
fs.mkdirSync( localStyleSourcesDir, { recursive: true } );
|
|
665
|
+
defaultStyleGitSources.forEach( ( source ) => {
|
|
666
|
+
if ( fileExists( source.dir ) ) {
|
|
667
|
+
runCommandOrThrow( 'git', [ '-C', source.dir, 'pull', '--ff-only' ] );
|
|
668
|
+
return;
|
|
669
|
+
}
|
|
670
|
+
runCommandOrThrow( 'git', [ 'clone', '--depth', '1', source.url, source.dir ] );
|
|
671
|
+
} );
|
|
672
|
+
return defaultStyleGitSources.map( ( source ) => source.dir );
|
|
673
|
+
}
|
|
674
|
+
|
|
575
675
|
function walkFiles( dirPath ) {
|
|
576
676
|
const entries = fs.readdirSync( dirPath, { withFileTypes: true } );
|
|
577
677
|
let result = [];
|
|
@@ -1393,16 +1493,11 @@ async function syncStyles( options ) {
|
|
|
1393
1493
|
options.repo :
|
|
1394
1494
|
path.join( rootDir, options.repo || 'vendor/styles' )
|
|
1395
1495
|
] :
|
|
1396
|
-
|
|
1496
|
+
ensureDefaultStyleSources();
|
|
1397
1497
|
const sourceDirs = configuredSourceDirs.filter( ( sourceDir ) => fileExists( sourceDir ) );
|
|
1398
1498
|
if ( options.repo && !sourceDirs.length ) {
|
|
1399
1499
|
throw new Error( `styles source not found: ${ configuredSourceDirs.join( ', ' ) }` );
|
|
1400
1500
|
}
|
|
1401
|
-
if ( !options.repo && !sourceDirs.length ) {
|
|
1402
|
-
throw new Error(
|
|
1403
|
-
'no bundled style sources found in this package; provide --repo <styles-dir> or run from a full source checkout'
|
|
1404
|
-
);
|
|
1405
|
-
}
|
|
1406
1501
|
|
|
1407
1502
|
let copiedCount = 0;
|
|
1408
1503
|
sourceDirs.forEach( ( sourceDir ) => {
|
|
@@ -4869,6 +4964,22 @@ function main() {
|
|
|
4869
4964
|
return;
|
|
4870
4965
|
}
|
|
4871
4966
|
|
|
4967
|
+
if ( action === 'translators' ) {
|
|
4968
|
+
const parsed = parseOptions( process.argv.slice( 3 ) );
|
|
4969
|
+
const subAction = parsed.args[ 0 ];
|
|
4970
|
+
if ( subAction !== 'sync' ) {
|
|
4971
|
+
usage();
|
|
4972
|
+
process.exit( 1 );
|
|
4973
|
+
}
|
|
4974
|
+
try {
|
|
4975
|
+
syncTranslators();
|
|
4976
|
+
process.exit( 0 );
|
|
4977
|
+
} catch ( error ) {
|
|
4978
|
+
handleCommandError( error, parsed, 'translators', 'sync' );
|
|
4979
|
+
}
|
|
4980
|
+
return;
|
|
4981
|
+
}
|
|
4982
|
+
|
|
4872
4983
|
if ( action === 'api' ) {
|
|
4873
4984
|
const parsed = parseOptions( process.argv.slice( 3 ) );
|
|
4874
4985
|
const requestPath = parsed.args.join( ' ' ).trim();
|