transduck 0.5.0 → 0.5.2
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/backend.js +20 -0
- package/dist/cli.d.ts +7 -0
- package/dist/cli.js +55 -1
- package/dist/storage.js +2 -2
- package/package.json +1 -1
- package/src/backend.ts +23 -0
- package/src/cli.ts +66 -1
- package/src/storage.ts +2 -2
package/dist/backend.js
CHANGED
|
@@ -4,11 +4,31 @@
|
|
|
4
4
|
import { getProvider } from './providers/index.js';
|
|
5
5
|
// Re-export prompts for backward compat (tests import buildMessages from backend)
|
|
6
6
|
export { buildMessages, buildPluralMessages } from './providers/prompts.js';
|
|
7
|
+
function checkApiKey(config) {
|
|
8
|
+
if (config.provider === 'claude_code') {
|
|
9
|
+
const token = process.env[config.tokenEnv];
|
|
10
|
+
if (!token) {
|
|
11
|
+
throw new Error(`Missing API token: ${config.tokenEnv} environment variable is not set.\n` +
|
|
12
|
+
`Run: export ${config.tokenEnv}=your-token-here`);
|
|
13
|
+
}
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
const apiKey = process.env[config.apiKeyEnv];
|
|
17
|
+
if (!apiKey) {
|
|
18
|
+
throw new Error(`Missing API key: ${config.apiKeyEnv} environment variable is not set.\n` +
|
|
19
|
+
`Run: export ${config.apiKeyEnv}=your-key-here\n` +
|
|
20
|
+
`Or add it to your .env file.`);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
7
23
|
export async function translate(sourceText, sourceLang, targetLang, projectContext, stringContext, config, _clientOverride) {
|
|
24
|
+
if (!_clientOverride)
|
|
25
|
+
checkApiKey(config);
|
|
8
26
|
const provider = await getProvider(config);
|
|
9
27
|
return provider.translate(sourceText, sourceLang, targetLang, projectContext, stringContext, config, _clientOverride);
|
|
10
28
|
}
|
|
11
29
|
export async function translatePlural(one, other, sourceLang, targetLang, projectContext, stringContext, config, _clientOverride) {
|
|
30
|
+
if (!_clientOverride)
|
|
31
|
+
checkApiKey(config);
|
|
12
32
|
const provider = await getProvider(config);
|
|
13
33
|
return provider.translatePlural(one, other, sourceLang, targetLang, projectContext, stringContext, config, _clientOverride);
|
|
14
34
|
}
|
package/dist/cli.d.ts
CHANGED
|
@@ -43,6 +43,13 @@ export declare function runScan(opts: ScanOptions): Promise<string>;
|
|
|
43
43
|
export interface StatsOptions {
|
|
44
44
|
configPath?: string;
|
|
45
45
|
}
|
|
46
|
+
export interface ClearOptions {
|
|
47
|
+
lang?: string;
|
|
48
|
+
failedOnly?: boolean;
|
|
49
|
+
yes?: boolean;
|
|
50
|
+
configPath?: string;
|
|
51
|
+
}
|
|
52
|
+
export declare function runClear(opts: ClearOptions): Promise<string>;
|
|
46
53
|
export declare function runStats(opts: StatsOptions): Promise<string>;
|
|
47
54
|
declare const program: Command;
|
|
48
55
|
export { program };
|
package/dist/cli.js
CHANGED
|
@@ -434,6 +434,45 @@ export async function runScan(opts) {
|
|
|
434
434
|
}
|
|
435
435
|
return lines.join('\n');
|
|
436
436
|
}
|
|
437
|
+
export async function runClear(opts) {
|
|
438
|
+
const cfg = loadConfig(opts.configPath);
|
|
439
|
+
const store = new TranslationStore(cfg.storagePath);
|
|
440
|
+
await store.initialize();
|
|
441
|
+
const targetLang = opts.lang?.toUpperCase();
|
|
442
|
+
const entryCount = store.count(targetLang, opts.failedOnly);
|
|
443
|
+
let desc;
|
|
444
|
+
if (targetLang && opts.failedOnly) {
|
|
445
|
+
desc = `${entryCount} failed translation(s) for ${targetLang}`;
|
|
446
|
+
}
|
|
447
|
+
else if (targetLang) {
|
|
448
|
+
desc = `${entryCount} translation(s) for ${targetLang}`;
|
|
449
|
+
}
|
|
450
|
+
else if (opts.failedOnly) {
|
|
451
|
+
desc = `${entryCount} failed translation(s) across all languages`;
|
|
452
|
+
}
|
|
453
|
+
else {
|
|
454
|
+
desc = `${entryCount} translation(s) across all languages`;
|
|
455
|
+
}
|
|
456
|
+
if (entryCount === 0) {
|
|
457
|
+
store.close();
|
|
458
|
+
return 'Nothing to clear.';
|
|
459
|
+
}
|
|
460
|
+
if (!opts.yes) {
|
|
461
|
+
const readline = await import('readline');
|
|
462
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
463
|
+
const answer = await new Promise(resolve => {
|
|
464
|
+
rl.question(`This will delete ${desc}. Type "Yes" to confirm: `, resolve);
|
|
465
|
+
});
|
|
466
|
+
rl.close();
|
|
467
|
+
if (answer !== 'Yes') {
|
|
468
|
+
store.close();
|
|
469
|
+
return 'Cancelled.';
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
await store.clear(targetLang, opts.failedOnly);
|
|
473
|
+
store.close();
|
|
474
|
+
return `Deleted ${desc}.`;
|
|
475
|
+
}
|
|
437
476
|
export async function runStats(opts) {
|
|
438
477
|
const cfg = loadConfig(opts.configPath);
|
|
439
478
|
const store = new TranslationStore(cfg.storagePath);
|
|
@@ -454,7 +493,7 @@ export async function runStats(opts) {
|
|
|
454
493
|
}
|
|
455
494
|
// CLI entry point
|
|
456
495
|
const program = new Command();
|
|
457
|
-
program.name('transduck').description('AI-native translation tool').version('0.5.
|
|
496
|
+
program.name('transduck').description('AI-native translation tool').version('0.5.2');
|
|
458
497
|
program.command('init')
|
|
459
498
|
.description('Initialize a new transduck project')
|
|
460
499
|
.action(async () => {
|
|
@@ -539,6 +578,21 @@ program.command('warm')
|
|
|
539
578
|
});
|
|
540
579
|
console.log(output);
|
|
541
580
|
});
|
|
581
|
+
program.command('clear')
|
|
582
|
+
.description('Clear cached translations from the database')
|
|
583
|
+
.option('--lang <code>', 'Only clear translations for this language')
|
|
584
|
+
.option('--failed-only', 'Only clear failed translations')
|
|
585
|
+
.option('-y, --yes', 'Skip confirmation prompt')
|
|
586
|
+
.option('--config <path>', 'Path to transduck.yaml')
|
|
587
|
+
.action(async (opts) => {
|
|
588
|
+
const output = await runClear({
|
|
589
|
+
lang: opts.lang,
|
|
590
|
+
failedOnly: opts.failedOnly,
|
|
591
|
+
yes: opts.yes,
|
|
592
|
+
configPath: opts.config ?? '',
|
|
593
|
+
});
|
|
594
|
+
console.log(output);
|
|
595
|
+
});
|
|
542
596
|
program.command('stats')
|
|
543
597
|
.description('Show translation database statistics')
|
|
544
598
|
.option('--config <path>', 'Path to transduck.yaml')
|
package/dist/storage.js
CHANGED
|
@@ -47,7 +47,7 @@ export class TranslationStore {
|
|
|
47
47
|
const db = this.getDb();
|
|
48
48
|
const chash = contentHash(params.sourceText, params.projectContextHash, params.stringContextHash);
|
|
49
49
|
db.prepare(`INSERT OR IGNORE INTO translations (source_lang, target_lang, content_hash, plural_category, source_text, translated_text, model, status, created_at, project_context_hash, string_context_hash, string_context)
|
|
50
|
-
VALUES (?, ?, ?, '', ?, ?, ?, ?, ?, ?, ?, ?)`).run(params.sourceLang, params.targetLang, chash, params.sourceText, params.translatedText, params.model, params.status, new Date().toISOString(), params.projectContextHash, params.stringContextHash, params.stringContext);
|
|
50
|
+
VALUES (?, ?, ?, '', ?, ?, ?, ?, ?, ?, ?, ?)`).run(params.sourceLang, params.targetLang, chash, params.sourceText, params.translatedText, params.model, params.status, new Date().toISOString(), params.projectContextHash, params.stringContextHash, params.stringContext ?? '');
|
|
51
51
|
}
|
|
52
52
|
async lookupPlural(params) {
|
|
53
53
|
const db = this.getDb();
|
|
@@ -64,7 +64,7 @@ export class TranslationStore {
|
|
|
64
64
|
const db = this.getDb();
|
|
65
65
|
const chash = contentHash(params.sourceText, params.projectContextHash, params.stringContextHash);
|
|
66
66
|
db.prepare(`INSERT OR IGNORE INTO translations (source_lang, target_lang, content_hash, plural_category, source_text, translated_text, model, status, created_at, project_context_hash, string_context_hash, string_context)
|
|
67
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(params.sourceLang, params.targetLang, chash, params.pluralCategory, params.sourceText, params.translatedText, params.model, params.status, new Date().toISOString(), params.projectContextHash, params.stringContextHash, params.stringContext);
|
|
67
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(params.sourceLang, params.targetLang, chash, params.pluralCategory, params.sourceText, params.translatedText, params.model, params.status, new Date().toISOString(), params.projectContextHash, params.stringContextHash, params.stringContext ?? '');
|
|
68
68
|
}
|
|
69
69
|
async stats() {
|
|
70
70
|
const db = this.getDb();
|
package/package.json
CHANGED
package/src/backend.ts
CHANGED
|
@@ -8,6 +8,27 @@ import { getProvider } from './providers/index.js';
|
|
|
8
8
|
// Re-export prompts for backward compat (tests import buildMessages from backend)
|
|
9
9
|
export { buildMessages, buildPluralMessages } from './providers/prompts.js';
|
|
10
10
|
|
|
11
|
+
function checkApiKey(config: TransduckConfig): void {
|
|
12
|
+
if (config.provider === 'claude_code') {
|
|
13
|
+
const token = process.env[config.tokenEnv];
|
|
14
|
+
if (!token) {
|
|
15
|
+
throw new Error(
|
|
16
|
+
`Missing API token: ${config.tokenEnv} environment variable is not set.\n` +
|
|
17
|
+
`Run: export ${config.tokenEnv}=your-token-here`
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
const apiKey = process.env[config.apiKeyEnv];
|
|
23
|
+
if (!apiKey) {
|
|
24
|
+
throw new Error(
|
|
25
|
+
`Missing API key: ${config.apiKeyEnv} environment variable is not set.\n` +
|
|
26
|
+
`Run: export ${config.apiKeyEnv}=your-key-here\n` +
|
|
27
|
+
`Or add it to your .env file.`
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
11
32
|
export async function translate(
|
|
12
33
|
sourceText: string,
|
|
13
34
|
sourceLang: string,
|
|
@@ -17,6 +38,7 @@ export async function translate(
|
|
|
17
38
|
config: TransduckConfig,
|
|
18
39
|
_clientOverride?: any,
|
|
19
40
|
): Promise<string> {
|
|
41
|
+
if (!_clientOverride) checkApiKey(config);
|
|
20
42
|
const provider = await getProvider(config);
|
|
21
43
|
return provider.translate(sourceText, sourceLang, targetLang, projectContext, stringContext, config, _clientOverride);
|
|
22
44
|
}
|
|
@@ -31,6 +53,7 @@ export async function translatePlural(
|
|
|
31
53
|
config: TransduckConfig,
|
|
32
54
|
_clientOverride?: any,
|
|
33
55
|
): Promise<Record<string, string>> {
|
|
56
|
+
if (!_clientOverride) checkApiKey(config);
|
|
34
57
|
const provider = await getProvider(config);
|
|
35
58
|
return provider.translatePlural(one, other, sourceLang, targetLang, projectContext, stringContext, config, _clientOverride);
|
|
36
59
|
}
|
package/src/cli.ts
CHANGED
|
@@ -539,6 +539,55 @@ export interface StatsOptions {
|
|
|
539
539
|
configPath?: string;
|
|
540
540
|
}
|
|
541
541
|
|
|
542
|
+
export interface ClearOptions {
|
|
543
|
+
lang?: string;
|
|
544
|
+
failedOnly?: boolean;
|
|
545
|
+
yes?: boolean;
|
|
546
|
+
configPath?: string;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
export async function runClear(opts: ClearOptions): Promise<string> {
|
|
550
|
+
const cfg = loadConfig(opts.configPath);
|
|
551
|
+
const store = new TranslationStore(cfg.storagePath);
|
|
552
|
+
await store.initialize();
|
|
553
|
+
|
|
554
|
+
const targetLang = opts.lang?.toUpperCase();
|
|
555
|
+
const entryCount = store.count(targetLang, opts.failedOnly);
|
|
556
|
+
|
|
557
|
+
let desc: string;
|
|
558
|
+
if (targetLang && opts.failedOnly) {
|
|
559
|
+
desc = `${entryCount} failed translation(s) for ${targetLang}`;
|
|
560
|
+
} else if (targetLang) {
|
|
561
|
+
desc = `${entryCount} translation(s) for ${targetLang}`;
|
|
562
|
+
} else if (opts.failedOnly) {
|
|
563
|
+
desc = `${entryCount} failed translation(s) across all languages`;
|
|
564
|
+
} else {
|
|
565
|
+
desc = `${entryCount} translation(s) across all languages`;
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
if (entryCount === 0) {
|
|
569
|
+
store.close();
|
|
570
|
+
return 'Nothing to clear.';
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
if (!opts.yes) {
|
|
574
|
+
const readline = await import('readline');
|
|
575
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
576
|
+
const answer = await new Promise<string>(resolve => {
|
|
577
|
+
rl.question(`This will delete ${desc}. Type "Yes" to confirm: `, resolve);
|
|
578
|
+
});
|
|
579
|
+
rl.close();
|
|
580
|
+
if (answer !== 'Yes') {
|
|
581
|
+
store.close();
|
|
582
|
+
return 'Cancelled.';
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
await store.clear(targetLang, opts.failedOnly);
|
|
587
|
+
store.close();
|
|
588
|
+
return `Deleted ${desc}.`;
|
|
589
|
+
}
|
|
590
|
+
|
|
542
591
|
export async function runStats(opts: StatsOptions): Promise<string> {
|
|
543
592
|
const cfg = loadConfig(opts.configPath);
|
|
544
593
|
const store = new TranslationStore(cfg.storagePath);
|
|
@@ -562,7 +611,7 @@ export async function runStats(opts: StatsOptions): Promise<string> {
|
|
|
562
611
|
// CLI entry point
|
|
563
612
|
const program = new Command();
|
|
564
613
|
|
|
565
|
-
program.name('transduck').description('AI-native translation tool').version('0.5.
|
|
614
|
+
program.name('transduck').description('AI-native translation tool').version('0.5.2');
|
|
566
615
|
|
|
567
616
|
program.command('init')
|
|
568
617
|
.description('Initialize a new transduck project')
|
|
@@ -659,6 +708,22 @@ program.command('warm')
|
|
|
659
708
|
console.log(output);
|
|
660
709
|
});
|
|
661
710
|
|
|
711
|
+
program.command('clear')
|
|
712
|
+
.description('Clear cached translations from the database')
|
|
713
|
+
.option('--lang <code>', 'Only clear translations for this language')
|
|
714
|
+
.option('--failed-only', 'Only clear failed translations')
|
|
715
|
+
.option('-y, --yes', 'Skip confirmation prompt')
|
|
716
|
+
.option('--config <path>', 'Path to transduck.yaml')
|
|
717
|
+
.action(async (opts: { lang?: string; failedOnly?: boolean; yes?: boolean; config?: string }) => {
|
|
718
|
+
const output = await runClear({
|
|
719
|
+
lang: opts.lang,
|
|
720
|
+
failedOnly: opts.failedOnly,
|
|
721
|
+
yes: opts.yes,
|
|
722
|
+
configPath: opts.config ?? '',
|
|
723
|
+
});
|
|
724
|
+
console.log(output);
|
|
725
|
+
});
|
|
726
|
+
|
|
662
727
|
program.command('stats')
|
|
663
728
|
.description('Show translation database statistics')
|
|
664
729
|
.option('--config <path>', 'Path to transduck.yaml')
|
package/src/storage.ts
CHANGED
|
@@ -90,7 +90,7 @@ export class TranslationStore {
|
|
|
90
90
|
params.sourceLang, params.targetLang, chash,
|
|
91
91
|
params.sourceText, params.translatedText, params.model, params.status,
|
|
92
92
|
new Date().toISOString(),
|
|
93
|
-
params.projectContextHash, params.stringContextHash, params.stringContext,
|
|
93
|
+
params.projectContextHash, params.stringContextHash, params.stringContext ?? '',
|
|
94
94
|
);
|
|
95
95
|
}
|
|
96
96
|
|
|
@@ -118,7 +118,7 @@ export class TranslationStore {
|
|
|
118
118
|
params.sourceLang, params.targetLang, chash, params.pluralCategory,
|
|
119
119
|
params.sourceText, params.translatedText, params.model, params.status,
|
|
120
120
|
new Date().toISOString(),
|
|
121
|
-
params.projectContextHash, params.stringContextHash, params.stringContext,
|
|
121
|
+
params.projectContextHash, params.stringContextHash, params.stringContext ?? '',
|
|
122
122
|
);
|
|
123
123
|
}
|
|
124
124
|
|