@daliusd/deepl-cli 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 daliusd
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,147 @@
1
+ # @daliusd/deepl-cli
2
+
3
+ CLI tool for translating text using the [DeepL API](https://www.deepl.com/docs-api). Pipe-friendly, configurable, supports context and formality options.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install -g @daliusd/deepl-cli
9
+ ```
10
+
11
+ Requires Node.js >= 18.
12
+
13
+ ## Configuration
14
+
15
+ Create a config file at `~/.config/deepl-cli/config.json`:
16
+
17
+ ```bash
18
+ mkdir -p ~/.config/deepl-cli
19
+ ```
20
+
21
+ ### Using a static API key
22
+
23
+ ```json
24
+ {
25
+ "api_key": "your-deepl-api-key"
26
+ }
27
+ ```
28
+
29
+ ### Using a command to retrieve the API key
30
+
31
+ This is useful with password managers like [pass](https://www.passwordstore.org/), 1Password CLI, etc.
32
+
33
+ ```json
34
+ {
35
+ "api_key_command": "pass show deepl-api-key"
36
+ }
37
+ ```
38
+
39
+ If both `api_key` and `api_key_command` are present, `api_key_command` takes precedence.
40
+
41
+ You can get a DeepL API key by creating a [DeepL API account](https://www.deepl.com/pro#developer) (free tier: 500,000 characters/month).
42
+
43
+ ## Usage
44
+
45
+ ```
46
+ deepl-cli [options] [text]
47
+ ```
48
+
49
+ ### Options
50
+
51
+ | Option | Short | Description |
52
+ |--------|-------|-------------|
53
+ | `--target <lang>` | `-t` | Target language code (required, e.g. `de`, `en-US`, `fr`) |
54
+ | `--source <lang>` | `-s` | Source language code (default: auto-detect) |
55
+ | `--context <text>` | `-c` | Additional context to influence translation (not translated, not billed) |
56
+ | `--formality <level>` | `-f` | Formality: `less`, `more`, `default`, `prefer_less`, `prefer_more` |
57
+ | `--verbose` | `-v` | Show metadata (detected source language, billed characters) |
58
+ | `--help` | `-h` | Show help |
59
+ | `--version` | | Show version |
60
+
61
+ ### Examples
62
+
63
+ Translate text to German:
64
+
65
+ ```bash
66
+ deepl-cli -t de "Hello, world!"
67
+ ```
68
+
69
+ Translate with context (context influences translation but is not translated or billed):
70
+
71
+ ```bash
72
+ deepl-cli -t de -c "This is a greeting in a formal business email" "Hello"
73
+ ```
74
+
75
+ Pipe text via stdin:
76
+
77
+ ```bash
78
+ echo "Hello, world!" | deepl-cli -t de
79
+ ```
80
+
81
+ Use formal language:
82
+
83
+ ```bash
84
+ deepl-cli -t de -f more "How are you?"
85
+ ```
86
+
87
+ Show translation metadata:
88
+
89
+ ```bash
90
+ deepl-cli -t de -v "Hello, world!"
91
+ # stdout: Hallo, Welt!
92
+ # stderr: Detected source language: en
93
+ # stderr: Billed characters: 13
94
+ ```
95
+
96
+ Specify source language explicitly:
97
+
98
+ ```bash
99
+ deepl-cli -t de -s en "Hello"
100
+ ```
101
+
102
+ ### Piping
103
+
104
+ The translated text is printed to stdout with no extra formatting, making it easy to use in pipelines:
105
+
106
+ ```bash
107
+ echo "Hello" | deepl-cli -t de | pbcopy
108
+ ```
109
+
110
+ When `--verbose` is used, metadata is written to stderr so stdout remains clean for piping.
111
+
112
+ ## Language codes
113
+
114
+ Language codes are case-insensitive and follow ISO 639-1, with some target languages including regional variants:
115
+
116
+ - Source: `en`, `de`, `fr`, `ja`, `es`, etc. (or omit for auto-detection)
117
+ - Target: `en-US`, `en-GB`, `pt-BR`, `pt-PT`, `de`, `fr`, etc.
118
+
119
+ See the [DeepL API documentation](https://www.deepl.com/docs-api/translating-text/) for the full list of supported languages.
120
+
121
+ ## Development
122
+
123
+ ```bash
124
+ git clone https://github.com/daliusd/deepl-cli.git
125
+ cd deepl-cli
126
+ npm install
127
+ npm run build
128
+ npm test
129
+ ```
130
+
131
+ ### Scripts
132
+
133
+ | Command | Description |
134
+ |---------|-------------|
135
+ | `npm run build` | Compile TypeScript to `dist/` |
136
+ | `npm test` | Run unit tests (`node:test`) |
137
+
138
+ ### Publishing
139
+
140
+ ```bash
141
+ npm run build
142
+ npm publish --access public
143
+ ```
144
+
145
+ ## License
146
+
147
+ [MIT](LICENSE)
package/dist/args.d.ts ADDED
@@ -0,0 +1,14 @@
1
+ import type { Formality } from 'deepl-node';
2
+ export declare const HELP_TEXT = "Usage: deepl-cli [options] [text]\n\nTranslate text using the DeepL API.\n\nArguments:\n text Text to translate (or pipe via stdin)\n\nOptions:\n -t, --target <lang> Target language code (required, e.g. \"de\", \"en-US\")\n -s, --source <lang> Source language code (default: auto-detect)\n -c, --context <text> Additional context for translation (not translated, not billed)\n -f, --formality <level> Formality: less, more, default, prefer_less, prefer_more\n -v, --verbose Show metadata (detected source lang, billed characters)\n -h, --help Show this help\n --version Show version\n\nExamples:\n deepl-cli -t de \"Hello, world!\"\n deepl-cli -t de -c \"Email greeting\" \"Hello\"\n echo \"Hello\" | deepl-cli -t de\n deepl-cli -t de -f more \"How are you?\"";
3
+ export interface ParsedArgs {
4
+ target: string;
5
+ source: string | null;
6
+ context: string | undefined;
7
+ formality: Formality | undefined;
8
+ verbose: boolean;
9
+ help: boolean;
10
+ version: boolean;
11
+ text: string | undefined;
12
+ }
13
+ export declare function parseCliArgs(args: string[]): ParsedArgs;
14
+ //# sourceMappingURL=args.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"args.d.ts","sourceRoot":"","sources":["../src/args.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAU5C,eAAO,MAAM,SAAS,21BAoBmB,CAAC;AAE1C,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,SAAS,EAAE,SAAS,GAAG,SAAS,CAAC;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;CAC1B;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,UAAU,CAkFvD"}
package/dist/args.js ADDED
@@ -0,0 +1,99 @@
1
+ import { parseArgs } from 'node:util';
2
+ const VALID_FORMALITY_VALUES = [
3
+ 'less',
4
+ 'more',
5
+ 'default',
6
+ 'prefer_less',
7
+ 'prefer_more',
8
+ ];
9
+ export const HELP_TEXT = `Usage: deepl-cli [options] [text]
10
+
11
+ Translate text using the DeepL API.
12
+
13
+ Arguments:
14
+ text Text to translate (or pipe via stdin)
15
+
16
+ Options:
17
+ -t, --target <lang> Target language code (required, e.g. "de", "en-US")
18
+ -s, --source <lang> Source language code (default: auto-detect)
19
+ -c, --context <text> Additional context for translation (not translated, not billed)
20
+ -f, --formality <level> Formality: less, more, default, prefer_less, prefer_more
21
+ -v, --verbose Show metadata (detected source lang, billed characters)
22
+ -h, --help Show this help
23
+ --version Show version
24
+
25
+ Examples:
26
+ deepl-cli -t de "Hello, world!"
27
+ deepl-cli -t de -c "Email greeting" "Hello"
28
+ echo "Hello" | deepl-cli -t de
29
+ deepl-cli -t de -f more "How are you?"`;
30
+ export function parseCliArgs(args) {
31
+ // Show help when called with no arguments
32
+ if (args.length === 0) {
33
+ return {
34
+ target: '',
35
+ source: null,
36
+ context: undefined,
37
+ formality: undefined,
38
+ verbose: false,
39
+ help: true,
40
+ version: false,
41
+ text: undefined,
42
+ };
43
+ }
44
+ const { values, positionals } = parseArgs({
45
+ args,
46
+ options: {
47
+ target: { type: 'string', short: 't' },
48
+ source: { type: 'string', short: 's' },
49
+ context: { type: 'string', short: 'c' },
50
+ formality: { type: 'string', short: 'f' },
51
+ verbose: { type: 'boolean', short: 'v', default: false },
52
+ help: { type: 'boolean', short: 'h', default: false },
53
+ version: { type: 'boolean', default: false },
54
+ },
55
+ allowPositionals: true,
56
+ });
57
+ if (values.help) {
58
+ return {
59
+ target: '',
60
+ source: null,
61
+ context: undefined,
62
+ formality: undefined,
63
+ verbose: false,
64
+ help: true,
65
+ version: false,
66
+ text: undefined,
67
+ };
68
+ }
69
+ if (values.version) {
70
+ return {
71
+ target: '',
72
+ source: null,
73
+ context: undefined,
74
+ formality: undefined,
75
+ verbose: false,
76
+ help: false,
77
+ version: true,
78
+ text: undefined,
79
+ };
80
+ }
81
+ if (!values.target) {
82
+ throw new Error('Missing required option: --target (-t)\nUse --help for usage information.');
83
+ }
84
+ if (values.formality &&
85
+ !VALID_FORMALITY_VALUES.includes(values.formality)) {
86
+ throw new Error(`Invalid formality value: "${values.formality}"\nValid values: ${VALID_FORMALITY_VALUES.join(', ')}`);
87
+ }
88
+ return {
89
+ target: values.target,
90
+ source: values.source || null,
91
+ context: values.context,
92
+ formality: values.formality,
93
+ verbose: values.verbose ?? false,
94
+ help: false,
95
+ version: false,
96
+ text: positionals.length > 0 ? positionals.join(' ') : undefined,
97
+ };
98
+ }
99
+ //# sourceMappingURL=args.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"args.js","sourceRoot":"","sources":["../src/args.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAGtC,MAAM,sBAAsB,GAAG;IAC7B,MAAM;IACN,MAAM;IACN,SAAS;IACT,aAAa;IACb,aAAa;CACL,CAAC;AAEX,MAAM,CAAC,MAAM,SAAS,GAAG;;;;;;;;;;;;;;;;;;;;yCAoBgB,CAAC;AAa1C,MAAM,UAAU,YAAY,CAAC,IAAc;IACzC,0CAA0C;IAC1C,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtB,OAAO;YACL,MAAM,EAAE,EAAE;YACV,MAAM,EAAE,IAAI;YACZ,OAAO,EAAE,SAAS;YAClB,SAAS,EAAE,SAAS;YACpB,OAAO,EAAE,KAAK;YACd,IAAI,EAAE,IAAI;YACV,OAAO,EAAE,KAAK;YACd,IAAI,EAAE,SAAS;SAChB,CAAC;IACJ,CAAC;IAED,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,SAAS,CAAC;QACxC,IAAI;QACJ,OAAO,EAAE;YACP,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,EAAE;YACtC,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,EAAE;YACtC,OAAO,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,EAAE;YACvC,SAAS,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,EAAE;YACzC,OAAO,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE;YACxD,IAAI,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE;YACrD,OAAO,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE;SAC7C;QACD,gBAAgB,EAAE,IAAI;KACvB,CAAC,CAAC;IAEH,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;QAChB,OAAO;YACL,MAAM,EAAE,EAAE;YACV,MAAM,EAAE,IAAI;YACZ,OAAO,EAAE,SAAS;YAClB,SAAS,EAAE,SAAS;YACpB,OAAO,EAAE,KAAK;YACd,IAAI,EAAE,IAAI;YACV,OAAO,EAAE,KAAK;YACd,IAAI,EAAE,SAAS;SAChB,CAAC;IACJ,CAAC;IAED,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;QACnB,OAAO;YACL,MAAM,EAAE,EAAE;YACV,MAAM,EAAE,IAAI;YACZ,OAAO,EAAE,SAAS;YAClB,SAAS,EAAE,SAAS;YACpB,OAAO,EAAE,KAAK;YACd,IAAI,EAAE,KAAK;YACX,OAAO,EAAE,IAAI;YACb,IAAI,EAAE,SAAS;SAChB,CAAC;IACJ,CAAC;IAED,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;QACnB,MAAM,IAAI,KAAK,CACb,2EAA2E,CAC5E,CAAC;IACJ,CAAC;IAED,IACE,MAAM,CAAC,SAAS;QAChB,CAAC,sBAAsB,CAAC,QAAQ,CAC9B,MAAM,CAAC,SAAoD,CAC5D,EACD,CAAC;QACD,MAAM,IAAI,KAAK,CACb,6BAA6B,MAAM,CAAC,SAAS,oBAAoB,sBAAsB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CACrG,CAAC;IACJ,CAAC;IAED,OAAO;QACL,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,MAAM,EAAG,MAAM,CAAC,MAAiB,IAAI,IAAI;QACzC,OAAO,EAAE,MAAM,CAAC,OAA6B;QAC7C,SAAS,EAAE,MAAM,CAAC,SAAkC;QACpD,OAAO,EAAE,MAAM,CAAC,OAAO,IAAI,KAAK;QAChC,IAAI,EAAE,KAAK;QACX,OAAO,EAAE,KAAK;QACd,IAAI,EAAE,WAAW,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS;KACjE,CAAC;AACJ,CAAC"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
package/dist/cli.js ADDED
@@ -0,0 +1,101 @@
1
+ #!/usr/bin/env node
2
+ import { createReadStream } from 'node:fs';
3
+ import { loadConfig, resolveApiKey } from './config.js';
4
+ import { createClient, translate } from './translate.js';
5
+ import { parseCliArgs, HELP_TEXT } from './args.js';
6
+ function readStdin() {
7
+ return new Promise((resolve, reject) => {
8
+ let data = '';
9
+ const stream = createReadStream('/dev/stdin', { encoding: 'utf-8' });
10
+ const timeout = setTimeout(() => {
11
+ stream.destroy();
12
+ reject(new Error('No input provided. Pass text as an argument or pipe via stdin.'));
13
+ }, 100);
14
+ stream.on('data', (chunk) => {
15
+ clearTimeout(timeout);
16
+ data += chunk;
17
+ });
18
+ stream.on('end', () => {
19
+ clearTimeout(timeout);
20
+ resolve(data.trim());
21
+ });
22
+ stream.on('error', (err) => {
23
+ clearTimeout(timeout);
24
+ reject(err);
25
+ });
26
+ });
27
+ }
28
+ function getVersion() {
29
+ return '0.1.0';
30
+ }
31
+ async function main() {
32
+ let parsed;
33
+ try {
34
+ parsed = parseCliArgs(process.argv.slice(2));
35
+ }
36
+ catch (err) {
37
+ process.stderr.write(`Error: ${err.message}\n`);
38
+ process.exit(1);
39
+ }
40
+ if (parsed.help) {
41
+ process.stdout.write(HELP_TEXT + '\n');
42
+ process.exit(0);
43
+ }
44
+ if (parsed.version) {
45
+ process.stdout.write(getVersion() + '\n');
46
+ process.exit(0);
47
+ }
48
+ // Get text from argument or stdin
49
+ let text = parsed.text;
50
+ if (!text) {
51
+ if (process.stdin.isTTY) {
52
+ process.stderr.write('Error: No text provided. Pass text as an argument or pipe via stdin.\n' +
53
+ 'Use --help for usage information.\n');
54
+ process.exit(1);
55
+ }
56
+ try {
57
+ text = await readStdin();
58
+ }
59
+ catch {
60
+ process.stderr.write('Error: No text provided. Pass text as an argument or pipe via stdin.\n' +
61
+ 'Use --help for usage information.\n');
62
+ process.exit(1);
63
+ }
64
+ }
65
+ if (!text) {
66
+ process.stderr.write('Error: Empty text provided.\nUse --help for usage information.\n');
67
+ process.exit(1);
68
+ }
69
+ // Load config and resolve API key
70
+ let apiKey;
71
+ try {
72
+ const config = loadConfig();
73
+ apiKey = resolveApiKey(config);
74
+ }
75
+ catch (err) {
76
+ process.stderr.write(`Error: ${err.message}\n`);
77
+ process.exit(1);
78
+ }
79
+ // Translate
80
+ try {
81
+ const client = createClient(apiKey);
82
+ const result = await translate(client, {
83
+ text,
84
+ sourceLang: parsed.source,
85
+ targetLang: parsed.target,
86
+ context: parsed.context,
87
+ formality: parsed.formality,
88
+ });
89
+ process.stdout.write(result.text + '\n');
90
+ if (parsed.verbose) {
91
+ process.stderr.write(`Detected source language: ${result.detectedSourceLang}\n`);
92
+ process.stderr.write(`Billed characters: ${result.billedCharacters}\n`);
93
+ }
94
+ }
95
+ catch (err) {
96
+ process.stderr.write(`Error: ${err.message}\n`);
97
+ process.exit(1);
98
+ }
99
+ }
100
+ main();
101
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ export interface Config {
2
+ api_key?: string;
3
+ api_key_command?: string;
4
+ }
5
+ export declare function loadConfig(configPath?: string): Config;
6
+ export declare function resolveApiKey(config: Config): string;
7
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,MAAM;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAKD,wBAAgB,UAAU,CAAC,UAAU,GAAE,MAA4B,GAAG,MAAM,CAoC3E;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAqBpD"}
package/dist/config.js ADDED
@@ -0,0 +1,55 @@
1
+ import { readFileSync, existsSync } from 'node:fs';
2
+ import { execSync } from 'node:child_process';
3
+ import { join } from 'node:path';
4
+ import { homedir } from 'node:os';
5
+ const DEFAULT_CONFIG_DIR = join(homedir(), '.config', 'deepl-cli');
6
+ const DEFAULT_CONFIG_PATH = join(DEFAULT_CONFIG_DIR, 'config.json');
7
+ export function loadConfig(configPath = DEFAULT_CONFIG_PATH) {
8
+ if (!existsSync(configPath)) {
9
+ throw new Error(`Config file not found: ${configPath}\n` +
10
+ `Create it with:\n` +
11
+ ` mkdir -p ~/.config/deepl-cli\n` +
12
+ ` echo '{"api_key": "your-deepl-api-key"}' > ~/.config/deepl-cli/config.json\n` +
13
+ `\n` +
14
+ `Or use api_key_command to retrieve the key from a password manager:\n` +
15
+ ` echo '{"api_key_command": "pass show deepl-api-key"}' > ~/.config/deepl-cli/config.json`);
16
+ }
17
+ let raw;
18
+ try {
19
+ raw = readFileSync(configPath, 'utf-8');
20
+ }
21
+ catch (err) {
22
+ throw new Error(`Failed to read config file: ${configPath}: ${err}`);
23
+ }
24
+ let config;
25
+ try {
26
+ config = JSON.parse(raw);
27
+ }
28
+ catch {
29
+ throw new Error(`Invalid JSON in config file: ${configPath}`);
30
+ }
31
+ if (!config.api_key && !config.api_key_command) {
32
+ throw new Error(`Config must contain "api_key" or "api_key_command".\n` +
33
+ ` api_key: your DeepL API key as a string\n` +
34
+ ` api_key_command: a shell command that outputs your API key (e.g. "pass show deepl-api-key")`);
35
+ }
36
+ return config;
37
+ }
38
+ export function resolveApiKey(config) {
39
+ if (config.api_key_command) {
40
+ try {
41
+ return execSync(config.api_key_command, {
42
+ encoding: 'utf-8',
43
+ stdio: ['pipe', 'pipe', 'pipe'],
44
+ }).trim();
45
+ }
46
+ catch (err) {
47
+ throw new Error(`Failed to execute api_key_command: ${config.api_key_command}\n${err}`);
48
+ }
49
+ }
50
+ if (config.api_key) {
51
+ return config.api_key;
52
+ }
53
+ throw new Error('No API key available. Set "api_key" or "api_key_command" in config.');
54
+ }
55
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAOlC,MAAM,kBAAkB,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,WAAW,CAAC,CAAC;AACnE,MAAM,mBAAmB,GAAG,IAAI,CAAC,kBAAkB,EAAE,aAAa,CAAC,CAAC;AAEpE,MAAM,UAAU,UAAU,CAAC,aAAqB,mBAAmB;IACjE,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC5B,MAAM,IAAI,KAAK,CACb,0BAA0B,UAAU,IAAI;YACtC,mBAAmB;YACnB,kCAAkC;YAClC,gFAAgF;YAChF,IAAI;YACJ,uEAAuE;YACvE,2FAA2F,CAC9F,CAAC;IACJ,CAAC;IAED,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACH,GAAG,GAAG,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;IAC1C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,+BAA+B,UAAU,KAAK,GAAG,EAAE,CAAC,CAAC;IACvE,CAAC;IAED,IAAI,MAAc,CAAC;IACnB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC3B,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,gCAAgC,UAAU,EAAE,CAAC,CAAC;IAChE,CAAC;IAED,IAAI,CAAC,MAAM,CAAC,OAAO,IAAI,CAAC,MAAM,CAAC,eAAe,EAAE,CAAC;QAC/C,MAAM,IAAI,KAAK,CACb,uDAAuD;YACrD,6CAA6C;YAC7C,+FAA+F,CAClG,CAAC;IACJ,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,MAAc;IAC1C,IAAI,MAAM,CAAC,eAAe,EAAE,CAAC;QAC3B,IAAI,CAAC;YACH,OAAO,QAAQ,CAAC,MAAM,CAAC,eAAe,EAAE;gBACtC,QAAQ,EAAE,OAAO;gBACjB,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;aAChC,CAAC,CAAC,IAAI,EAAE,CAAC;QACZ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,IAAI,KAAK,CACb,sCAAsC,MAAM,CAAC,eAAe,KAAK,GAAG,EAAE,CACvE,CAAC;QACJ,CAAC;IACH,CAAC;IAED,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;QACnB,OAAO,MAAM,CAAC,OAAO,CAAC;IACxB,CAAC;IAED,MAAM,IAAI,KAAK,CACb,qEAAqE,CACtE,CAAC;AACJ,CAAC"}
@@ -0,0 +1,16 @@
1
+ import * as deepl from 'deepl-node';
2
+ export interface TranslateOptions {
3
+ text: string;
4
+ sourceLang: deepl.SourceLanguageCode | null;
5
+ targetLang: deepl.TargetLanguageCode;
6
+ context?: string;
7
+ formality?: deepl.Formality;
8
+ }
9
+ export interface TranslateResult {
10
+ text: string;
11
+ detectedSourceLang: string;
12
+ billedCharacters: number;
13
+ }
14
+ export declare function createClient(apiKey: string): deepl.DeepLClient;
15
+ export declare function translate(client: deepl.DeepLClient, options: TranslateOptions): Promise<TranslateResult>;
16
+ //# sourceMappingURL=translate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"translate.d.ts","sourceRoot":"","sources":["../src/translate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,YAAY,CAAC;AAEpC,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,KAAK,CAAC,kBAAkB,GAAG,IAAI,CAAC;IAC5C,UAAU,EAAE,KAAK,CAAC,kBAAkB,CAAC;IACrC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC7B;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,kBAAkB,EAAE,MAAM,CAAC;IAC3B,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,KAAK,CAAC,WAAW,CAO9D;AAED,wBAAsB,SAAS,CAC7B,MAAM,EAAE,KAAK,CAAC,WAAW,EACzB,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC,eAAe,CAAC,CAyB1B"}
@@ -0,0 +1,26 @@
1
+ import * as deepl from 'deepl-node';
2
+ export function createClient(apiKey) {
3
+ return new deepl.DeepLClient(apiKey, {
4
+ appInfo: {
5
+ appName: 'deepl-cli',
6
+ appVersion: '0.1.0',
7
+ },
8
+ });
9
+ }
10
+ export async function translate(client, options) {
11
+ const translateOptions = {};
12
+ if (options.context) {
13
+ translateOptions.context = options.context;
14
+ }
15
+ if (options.formality) {
16
+ translateOptions.formality = options.formality;
17
+ }
18
+ const result = await client.translateText(options.text, options.sourceLang, options.targetLang, translateOptions);
19
+ const single = Array.isArray(result) ? result[0] : result;
20
+ return {
21
+ text: single.text,
22
+ detectedSourceLang: single.detectedSourceLang,
23
+ billedCharacters: single.billedCharacters,
24
+ };
25
+ }
26
+ //# sourceMappingURL=translate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"translate.js","sourceRoot":"","sources":["../src/translate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,YAAY,CAAC;AAgBpC,MAAM,UAAU,YAAY,CAAC,MAAc;IACzC,OAAO,IAAI,KAAK,CAAC,WAAW,CAAC,MAAM,EAAE;QACnC,OAAO,EAAE;YACP,OAAO,EAAE,WAAW;YACpB,UAAU,EAAE,OAAO;SACpB;KACF,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,MAAyB,EACzB,OAAyB;IAEzB,MAAM,gBAAgB,GAA+B,EAAE,CAAC;IAExD,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;QACpB,gBAAgB,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IAC7C,CAAC;IAED,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;QACtB,gBAAgB,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC;IACjD,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,aAAa,CACvC,OAAO,CAAC,IAAI,EACZ,OAAO,CAAC,UAAU,EAClB,OAAO,CAAC,UAAU,EAClB,gBAAgB,CACjB,CAAC;IAEF,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;IAE1D,OAAO;QACL,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,kBAAkB,EAAE,MAAM,CAAC,kBAAkB;QAC7C,gBAAgB,EAAE,MAAM,CAAC,gBAAgB;KAC1C,CAAC;AACJ,CAAC"}
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "@daliusd/deepl-cli",
3
+ "version": "0.1.0",
4
+ "description": "CLI tool for translating text using the DeepL API",
5
+ "type": "module",
6
+ "bin": {
7
+ "deepl-cli": "dist/cli.js"
8
+ },
9
+ "files": [
10
+ "dist"
11
+ ],
12
+ "scripts": {
13
+ "build": "tsc",
14
+ "test": "node --import tsx --test tests/*.test.ts"
15
+ },
16
+ "engines": {
17
+ "node": ">=18"
18
+ },
19
+ "keywords": [
20
+ "deepl",
21
+ "translate",
22
+ "translation",
23
+ "cli"
24
+ ],
25
+ "author": "daliusd",
26
+ "license": "MIT",
27
+ "publishConfig": {
28
+ "access": "public"
29
+ },
30
+ "dependencies": {
31
+ "deepl-node": "^1.15.0"
32
+ },
33
+ "devDependencies": {
34
+ "tsx": "^4.19.0",
35
+ "typescript": "^5.7.0",
36
+ "@types/node": "^22.0.0"
37
+ }
38
+ }