@kopynator/cli 1.4.1 → 1.5.1
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/index.js +83 -3
- package/package.json +2 -2
- package/src/commands/index.ts +1 -0
- package/src/commands/limits.ts +110 -0
- package/src/index.ts +7 -2
package/dist/index.js
CHANGED
|
@@ -25,7 +25,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
25
25
|
|
|
26
26
|
// src/index.ts
|
|
27
27
|
var import_commander = require("commander");
|
|
28
|
-
var
|
|
28
|
+
var import_chalk6 = __toESM(require("chalk"));
|
|
29
29
|
|
|
30
30
|
// src/commands/init.ts
|
|
31
31
|
var import_inquirer = __toESM(require("inquirer"));
|
|
@@ -689,15 +689,95 @@ async function uploadCommand(options) {
|
|
|
689
689
|
}
|
|
690
690
|
}
|
|
691
691
|
|
|
692
|
+
// src/commands/limits.ts
|
|
693
|
+
var import_chalk5 = __toESM(require("chalk"));
|
|
694
|
+
var import_fs5 = __toESM(require("fs"));
|
|
695
|
+
var import_path5 = __toESM(require("path"));
|
|
696
|
+
function resolveApiKey() {
|
|
697
|
+
const cwd = process.cwd();
|
|
698
|
+
const fromJson = (p) => {
|
|
699
|
+
try {
|
|
700
|
+
if (import_fs5.default.existsSync(p)) {
|
|
701
|
+
const cfg = JSON.parse(import_fs5.default.readFileSync(p, "utf-8"));
|
|
702
|
+
if (cfg && cfg.apiKey) return { apiKey: cfg.apiKey, baseUrl: cfg.baseUrl };
|
|
703
|
+
}
|
|
704
|
+
} catch {
|
|
705
|
+
}
|
|
706
|
+
return null;
|
|
707
|
+
};
|
|
708
|
+
const fromAppFile = (p) => {
|
|
709
|
+
try {
|
|
710
|
+
if (import_fs5.default.existsSync(p)) {
|
|
711
|
+
const content = import_fs5.default.readFileSync(p, "utf-8");
|
|
712
|
+
const match = content.match(/apiKey:\s*['"]([^'"]+)['"]/);
|
|
713
|
+
if (match) return { apiKey: match[1] };
|
|
714
|
+
}
|
|
715
|
+
} catch {
|
|
716
|
+
}
|
|
717
|
+
return null;
|
|
718
|
+
};
|
|
719
|
+
return fromJson(import_path5.default.join(cwd, "kopynator.config.json")) || fromJson(import_path5.default.join(cwd, "src/kopynator.config.json")) || fromAppFile(import_path5.default.join(cwd, "src/app/app.config.ts")) || fromAppFile(import_path5.default.join(cwd, "src/app/app.module.ts")) || (process.env.KOPYNATOR_API_KEY ? { apiKey: process.env.KOPYNATOR_API_KEY.trim(), baseUrl: process.env.KOPYNATOR_BASE_URL?.trim() } : null);
|
|
720
|
+
}
|
|
721
|
+
function formatLimit(value) {
|
|
722
|
+
return value === -1 ? "Unlimited" : String(value);
|
|
723
|
+
}
|
|
724
|
+
async function limitsCommand() {
|
|
725
|
+
const resolved = resolveApiKey();
|
|
726
|
+
if (!resolved) {
|
|
727
|
+
console.log(import_chalk5.default.red("\n\u2716 No API key found."));
|
|
728
|
+
console.log(import_chalk5.default.gray(" Add it to kopynator.config.json or set KOPYNATOR_API_KEY.\n"));
|
|
729
|
+
process.exit(1);
|
|
730
|
+
return;
|
|
731
|
+
}
|
|
732
|
+
const base = (resolved.baseUrl || process.env.KOPYNATOR_BASE_URL || "https://api.kopynator.com").replace(/\/+$/, "").replace(/\/api$/, "");
|
|
733
|
+
const url = `${base}/tokens/limits?token=${encodeURIComponent(resolved.apiKey)}`;
|
|
734
|
+
try {
|
|
735
|
+
const res = await fetch(url, { headers: { "x-kopynator-version": "1.5.0" } });
|
|
736
|
+
if (!res.ok) {
|
|
737
|
+
const body = await res.text().catch(() => "");
|
|
738
|
+
console.log(import_chalk5.default.red(`
|
|
739
|
+
\u2716 Could not fetch limits (HTTP ${res.status}). ${body}
|
|
740
|
+
`));
|
|
741
|
+
process.exit(1);
|
|
742
|
+
return;
|
|
743
|
+
}
|
|
744
|
+
const data = await res.json();
|
|
745
|
+
const limits = data.limits || {};
|
|
746
|
+
const usage = data.usage || {};
|
|
747
|
+
const row = (label, used, limit) => {
|
|
748
|
+
const usedPart = used === void 0 ? "" : `${used} / `;
|
|
749
|
+
const text = ` ${label.padEnd(9)} ${usedPart}${formatLimit(limit)}`;
|
|
750
|
+
const reached = limit !== -1 && used !== void 0 && used >= limit;
|
|
751
|
+
return reached ? import_chalk5.default.red(`${text} (limit reached \u2014 upgrade your plan)`) : import_chalk5.default.green(text);
|
|
752
|
+
};
|
|
753
|
+
console.log("");
|
|
754
|
+
console.log(import_chalk5.default.bold("\u{1F4CA} Kopynator \u2014 plan limits (per organization)"));
|
|
755
|
+
const planLabel = import_chalk5.default.cyan((data.plan || "free").toUpperCase());
|
|
756
|
+
const statusLabel = data.active ? import_chalk5.default.green("active") : import_chalk5.default.yellow("inactive/expired \u2192 free limits apply");
|
|
757
|
+
console.log(` Plan: ${planLabel} (${statusLabel})`);
|
|
758
|
+
console.log("");
|
|
759
|
+
console.log(row("Projects", usage.projects, limits.projects));
|
|
760
|
+
console.log(row("Keys", usage.keys, limits.keys));
|
|
761
|
+
console.log(row("Members", void 0, limits.members));
|
|
762
|
+
console.log("");
|
|
763
|
+
} catch (error) {
|
|
764
|
+
console.log(import_chalk5.default.red(`
|
|
765
|
+
\u2716 Request failed: ${error?.message || error}
|
|
766
|
+
`));
|
|
767
|
+
process.exit(1);
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
|
|
692
771
|
// src/index.ts
|
|
693
772
|
var program = new import_commander.Command();
|
|
694
|
-
program.name("kopynator").description("Kopynator CLI - Manage your i18n workflow").version("1.
|
|
773
|
+
program.name("kopynator").description("Kopynator CLI - Manage your i18n workflow").version("1.5.1", "-v, --version").helpOption("-h, --help", "Display help for command").addHelpText("beforeAll", import_chalk6.default.blue("\n\u{1F44B} Welcome to Kopynator CLI!\n"));
|
|
695
774
|
program.command("init").description("Initialize Kopynator in your project").action(initCommand);
|
|
696
775
|
program.command("check").description("Validate your local JSON translation files").action(checkCommand);
|
|
697
776
|
program.command("sync").description("Sync your translations with the Kopynator Cloud").action(syncCommand);
|
|
698
777
|
program.command("upload").description("Upload a JSON translation file to Kopynator Cloud").option("-f, --file <path>", "Path to the JSON file (e.g. es.json)").option("-l, --lang <code>", "Language code (default: inferred from filename)").action((opts) => uploadCommand({ file: opts.file, lang: opts.lang }));
|
|
778
|
+
program.command("limits").description("Show your plan limits and current usage (projects, keys, members)").action(limitsCommand);
|
|
699
779
|
program.command("help").description("Show help for all commands").action(() => {
|
|
700
|
-
console.log(
|
|
780
|
+
console.log(import_chalk6.default.blue("\u{1F44B} Kopynator CLI - Comandos disponibles:\n"));
|
|
701
781
|
program.outputHelp();
|
|
702
782
|
});
|
|
703
783
|
program.parse(process.argv);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kopynator/cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.1",
|
|
4
4
|
"description": "CLI tool for Kopynator - The i18n management solution",
|
|
5
5
|
"bin": {
|
|
6
6
|
"kopynator": "dist/index.js"
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"author": "Carlos Florio",
|
|
24
24
|
"license": "ISC",
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@kopynator/core": "^
|
|
26
|
+
"@kopynator/core": "^2.0.0",
|
|
27
27
|
"chalk": "^4.1.2",
|
|
28
28
|
"commander": "^11.1.0",
|
|
29
29
|
"inquirer": "^8.2.6",
|
package/src/commands/index.ts
CHANGED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
|
|
5
|
+
interface ResolvedKey {
|
|
6
|
+
apiKey: string;
|
|
7
|
+
baseUrl?: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Resolve the API key the same way `sync`/`upload` do: config JSON first, then env.
|
|
12
|
+
*/
|
|
13
|
+
function resolveApiKey(): ResolvedKey | null {
|
|
14
|
+
const cwd = process.cwd();
|
|
15
|
+
|
|
16
|
+
const fromJson = (p: string): ResolvedKey | null => {
|
|
17
|
+
try {
|
|
18
|
+
if (fs.existsSync(p)) {
|
|
19
|
+
const cfg = JSON.parse(fs.readFileSync(p, 'utf-8'));
|
|
20
|
+
if (cfg && cfg.apiKey) return { apiKey: cfg.apiKey, baseUrl: cfg.baseUrl };
|
|
21
|
+
}
|
|
22
|
+
} catch {
|
|
23
|
+
/* ignore malformed config */
|
|
24
|
+
}
|
|
25
|
+
return null;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const fromAppFile = (p: string): ResolvedKey | null => {
|
|
29
|
+
try {
|
|
30
|
+
if (fs.existsSync(p)) {
|
|
31
|
+
const content = fs.readFileSync(p, 'utf-8');
|
|
32
|
+
const match = content.match(/apiKey:\s*['"]([^'"]+)['"]/);
|
|
33
|
+
if (match) return { apiKey: match[1] };
|
|
34
|
+
}
|
|
35
|
+
} catch {
|
|
36
|
+
/* ignore */
|
|
37
|
+
}
|
|
38
|
+
return null;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
fromJson(path.join(cwd, 'kopynator.config.json')) ||
|
|
43
|
+
fromJson(path.join(cwd, 'src/kopynator.config.json')) ||
|
|
44
|
+
fromAppFile(path.join(cwd, 'src/app/app.config.ts')) ||
|
|
45
|
+
fromAppFile(path.join(cwd, 'src/app/app.module.ts')) ||
|
|
46
|
+
(process.env.KOPYNATOR_API_KEY
|
|
47
|
+
? { apiKey: process.env.KOPYNATOR_API_KEY.trim(), baseUrl: process.env.KOPYNATOR_BASE_URL?.trim() }
|
|
48
|
+
: null)
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function formatLimit(value: number): string {
|
|
53
|
+
return value === -1 ? 'Unlimited' : String(value);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* `kopynator limits` — show the current plan, its per-organization limits and usage.
|
|
58
|
+
*/
|
|
59
|
+
export async function limitsCommand(): Promise<void> {
|
|
60
|
+
const resolved = resolveApiKey();
|
|
61
|
+
if (!resolved) {
|
|
62
|
+
console.log(chalk.red('\n✖ No API key found.'));
|
|
63
|
+
console.log(chalk.gray(' Add it to kopynator.config.json or set KOPYNATOR_API_KEY.\n'));
|
|
64
|
+
process.exit(1);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Endpoint lives outside the /api prefix (like /tokens/fetch), so normalise a trailing /api away.
|
|
69
|
+
const base = (resolved.baseUrl || process.env.KOPYNATOR_BASE_URL || 'https://api.kopynator.com')
|
|
70
|
+
.replace(/\/+$/, '')
|
|
71
|
+
.replace(/\/api$/, '');
|
|
72
|
+
const url = `${base}/tokens/limits?token=${encodeURIComponent(resolved.apiKey)}`;
|
|
73
|
+
|
|
74
|
+
try {
|
|
75
|
+
const res = await fetch(url, { headers: { 'x-kopynator-version': '1.5.0' } });
|
|
76
|
+
if (!res.ok) {
|
|
77
|
+
const body = await res.text().catch(() => '');
|
|
78
|
+
console.log(chalk.red(`\n✖ Could not fetch limits (HTTP ${res.status}). ${body}\n`));
|
|
79
|
+
process.exit(1);
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const data: any = await res.json();
|
|
84
|
+
const limits = data.limits || {};
|
|
85
|
+
const usage = data.usage || {};
|
|
86
|
+
|
|
87
|
+
const row = (label: string, used: number | undefined, limit: number): string => {
|
|
88
|
+
const usedPart = used === undefined ? '' : `${used} / `;
|
|
89
|
+
const text = ` ${label.padEnd(9)} ${usedPart}${formatLimit(limit)}`;
|
|
90
|
+
const reached = limit !== -1 && used !== undefined && used >= limit;
|
|
91
|
+
return reached ? chalk.red(`${text} (limit reached — upgrade your plan)`) : chalk.green(text);
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
console.log('');
|
|
95
|
+
console.log(chalk.bold('📊 Kopynator — plan limits (per organization)'));
|
|
96
|
+
const planLabel = chalk.cyan((data.plan || 'free').toUpperCase());
|
|
97
|
+
const statusLabel = data.active
|
|
98
|
+
? chalk.green('active')
|
|
99
|
+
: chalk.yellow('inactive/expired → free limits apply');
|
|
100
|
+
console.log(` Plan: ${planLabel} (${statusLabel})`);
|
|
101
|
+
console.log('');
|
|
102
|
+
console.log(row('Projects', usage.projects, limits.projects));
|
|
103
|
+
console.log(row('Keys', usage.keys, limits.keys));
|
|
104
|
+
console.log(row('Members', undefined, limits.members));
|
|
105
|
+
console.log('');
|
|
106
|
+
} catch (error: any) {
|
|
107
|
+
console.log(chalk.red(`\n✖ Request failed: ${error?.message || error}\n`));
|
|
108
|
+
process.exit(1);
|
|
109
|
+
}
|
|
110
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { Command } from 'commander';
|
|
3
3
|
import chalk from 'chalk';
|
|
4
|
-
import { initCommand, checkCommand, syncCommand, uploadCommand } from './commands';
|
|
4
|
+
import { initCommand, checkCommand, syncCommand, uploadCommand, limitsCommand } from './commands';
|
|
5
5
|
|
|
6
6
|
const program = new Command();
|
|
7
7
|
|
|
8
8
|
program
|
|
9
9
|
.name('kopynator')
|
|
10
10
|
.description('Kopynator CLI - Manage your i18n workflow')
|
|
11
|
-
.version('1.
|
|
11
|
+
.version('1.5.1', '-v, --version')
|
|
12
12
|
.helpOption('-h, --help', 'Display help for command')
|
|
13
13
|
.addHelpText('beforeAll', chalk.blue('\n👋 Welcome to Kopynator CLI!\n'));
|
|
14
14
|
|
|
@@ -34,6 +34,11 @@ program
|
|
|
34
34
|
.option('-l, --lang <code>', 'Language code (default: inferred from filename)')
|
|
35
35
|
.action((opts) => uploadCommand({ file: opts.file, lang: opts.lang }));
|
|
36
36
|
|
|
37
|
+
program
|
|
38
|
+
.command('limits')
|
|
39
|
+
.description('Show your plan limits and current usage (projects, keys, members)')
|
|
40
|
+
.action(limitsCommand);
|
|
41
|
+
|
|
37
42
|
program
|
|
38
43
|
.command('help')
|
|
39
44
|
.description('Show help for all commands')
|