@ownlate/cli 1.0.0 → 1.0.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/index.cjs +232 -74
- package/package.json +4 -3
package/dist/index.cjs
CHANGED
|
@@ -2,8 +2,193 @@
|
|
|
2
2
|
let commander = require("commander");
|
|
3
3
|
let node_readline_promises = require("node:readline/promises");
|
|
4
4
|
let node_fs_promises = require("node:fs/promises");
|
|
5
|
+
let node_os = require("node:os");
|
|
5
6
|
let node_path = require("node:path");
|
|
6
7
|
|
|
8
|
+
//#region src/lib/api-client.ts
|
|
9
|
+
var ApiClient = class {
|
|
10
|
+
constructor(baseUrl, apiKey) {
|
|
11
|
+
this.baseUrl = baseUrl.replace(/\/$/, "");
|
|
12
|
+
this.apiKey = apiKey;
|
|
13
|
+
}
|
|
14
|
+
authHeaders() {
|
|
15
|
+
return { Authorization: `Bearer ${this.apiKey}` };
|
|
16
|
+
}
|
|
17
|
+
async request(path, init) {
|
|
18
|
+
const res = await fetch(`${this.baseUrl}${path}`, {
|
|
19
|
+
...init,
|
|
20
|
+
headers: {
|
|
21
|
+
...this.authHeaders(),
|
|
22
|
+
...init?.headers ?? {}
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
if (!res.ok) {
|
|
26
|
+
const text = await res.text();
|
|
27
|
+
throw new Error(`HTTP ${res.status}: ${text}`);
|
|
28
|
+
}
|
|
29
|
+
return res.json();
|
|
30
|
+
}
|
|
31
|
+
async listWorkspaces() {
|
|
32
|
+
return this.request("/v1/workspaces");
|
|
33
|
+
}
|
|
34
|
+
async listProjects(workspaceId) {
|
|
35
|
+
return this.request(`/v1/projects?workspaceId=${encodeURIComponent(workspaceId)}`);
|
|
36
|
+
}
|
|
37
|
+
async upload(dto) {
|
|
38
|
+
return this.request("/v1/translation-files/upload", {
|
|
39
|
+
method: "POST",
|
|
40
|
+
headers: { "Content-Type": "application/json" },
|
|
41
|
+
body: JSON.stringify(dto)
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
async exportTranslations(params) {
|
|
45
|
+
const query = new URLSearchParams({
|
|
46
|
+
projectId: params.projectId,
|
|
47
|
+
language: params.language,
|
|
48
|
+
format: params.format
|
|
49
|
+
});
|
|
50
|
+
if (params.fileId) query.set("fileId", params.fileId);
|
|
51
|
+
if (params.sourceLanguage) query.set("sourceLanguage", params.sourceLanguage);
|
|
52
|
+
const res = await fetch(`${this.baseUrl}/v1/segments/export?${query}`, { headers: this.authHeaders() });
|
|
53
|
+
if (!res.ok) {
|
|
54
|
+
const text = await res.text();
|
|
55
|
+
throw new Error(`HTTP ${res.status}: ${text}`);
|
|
56
|
+
}
|
|
57
|
+
return res.text();
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
//#endregion
|
|
62
|
+
//#region src/lib/global-config.ts
|
|
63
|
+
const CONFIG_PATH = (0, node_path.join)((0, node_os.homedir)(), ".ownlate");
|
|
64
|
+
const DEFAULT_API_URL = "https://api.ownlate.com";
|
|
65
|
+
async function readGlobalConfig() {
|
|
66
|
+
try {
|
|
67
|
+
const raw = await (0, node_fs_promises.readFile)(CONFIG_PATH, "utf-8");
|
|
68
|
+
return JSON.parse(raw);
|
|
69
|
+
} catch {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
async function writeGlobalConfig(config) {
|
|
74
|
+
await (0, node_fs_promises.writeFile)(CONFIG_PATH, JSON.stringify(config, null, 2) + "\n", { mode: 384 });
|
|
75
|
+
}
|
|
76
|
+
async function requireGlobalConfig() {
|
|
77
|
+
const config = await readGlobalConfig();
|
|
78
|
+
if (!config?.apiKey) throw new Error("Not logged in. Run \"ownlate login\" first.");
|
|
79
|
+
return config;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
//#endregion
|
|
83
|
+
//#region src/commands/login.ts
|
|
84
|
+
async function ask$1(rl, question, fallback = "") {
|
|
85
|
+
return (await rl.question(question)).trim() || fallback;
|
|
86
|
+
}
|
|
87
|
+
async function loginCommand(options) {
|
|
88
|
+
const rl = (0, node_readline_promises.createInterface)({
|
|
89
|
+
input: process.stdin,
|
|
90
|
+
output: process.stdout
|
|
91
|
+
});
|
|
92
|
+
try {
|
|
93
|
+
const apiUrl = options.url ?? await ask$1(rl, `API URL [${DEFAULT_API_URL}]: `, DEFAULT_API_URL);
|
|
94
|
+
const apiKey = await ask$1(rl, "API Key: ");
|
|
95
|
+
if (!apiKey) {
|
|
96
|
+
console.error("API Key is required.");
|
|
97
|
+
process.exit(1);
|
|
98
|
+
}
|
|
99
|
+
process.stdout.write("\nConnecting...");
|
|
100
|
+
const client = new ApiClient(apiUrl, apiKey);
|
|
101
|
+
let workspaces;
|
|
102
|
+
try {
|
|
103
|
+
workspaces = await client.listWorkspaces();
|
|
104
|
+
console.log(" ok");
|
|
105
|
+
} catch (err) {
|
|
106
|
+
console.log(" failed");
|
|
107
|
+
console.error(`\n${err.message}`);
|
|
108
|
+
process.exit(1);
|
|
109
|
+
}
|
|
110
|
+
let currentWorkspaceId;
|
|
111
|
+
if (workspaces.length === 0) console.log("\nNo workspaces found. Create one in the web app first.");
|
|
112
|
+
else if (workspaces.length === 1) {
|
|
113
|
+
currentWorkspaceId = workspaces[0].id;
|
|
114
|
+
console.log(`\nUsing workspace: ${workspaces[0].name}`);
|
|
115
|
+
} else {
|
|
116
|
+
console.log("\nAvailable workspaces:");
|
|
117
|
+
workspaces.forEach((w, i) => console.log(` ${i + 1}. ${w.name}`));
|
|
118
|
+
const choice = await ask$1(rl, "\nSelect workspace [1]: ", "1");
|
|
119
|
+
const idx = parseInt(choice, 10) - 1;
|
|
120
|
+
const picked = workspaces[Math.max(0, Math.min(idx, workspaces.length - 1))];
|
|
121
|
+
currentWorkspaceId = picked.id;
|
|
122
|
+
console.log(`Using: ${picked.name}`);
|
|
123
|
+
}
|
|
124
|
+
await writeGlobalConfig({
|
|
125
|
+
apiUrl,
|
|
126
|
+
apiKey,
|
|
127
|
+
currentWorkspaceId
|
|
128
|
+
});
|
|
129
|
+
console.log("\nLogged in. Run \"ownlate init\" to set up a project.");
|
|
130
|
+
} finally {
|
|
131
|
+
rl.close();
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
//#endregion
|
|
136
|
+
//#region src/commands/logout.ts
|
|
137
|
+
async function logoutCommand() {
|
|
138
|
+
try {
|
|
139
|
+
await (0, node_fs_promises.unlink)((0, node_path.join)((0, node_os.homedir)(), ".ownlate"));
|
|
140
|
+
console.log("Logged out.");
|
|
141
|
+
} catch {
|
|
142
|
+
console.log("Already logged out.");
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
//#endregion
|
|
147
|
+
//#region src/commands/workspace.ts
|
|
148
|
+
async function workspaceListCommand() {
|
|
149
|
+
const auth = await requireGlobalConfig();
|
|
150
|
+
const workspaces = await new ApiClient(auth.apiUrl, auth.apiKey).listWorkspaces();
|
|
151
|
+
if (workspaces.length === 0) {
|
|
152
|
+
console.log("No workspaces found.");
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
for (const w of workspaces) {
|
|
156
|
+
const marker = w.id === auth.currentWorkspaceId ? "* " : " ";
|
|
157
|
+
console.log(`${marker}${w.name} ${w.id}`);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
async function workspaceUseCommand(idOrName) {
|
|
161
|
+
const auth = await requireGlobalConfig();
|
|
162
|
+
const found = (await new ApiClient(auth.apiUrl, auth.apiKey).listWorkspaces()).find((w) => w.id === idOrName || w.name === idOrName);
|
|
163
|
+
if (!found) {
|
|
164
|
+
console.error(`Workspace not found: "${idOrName}"`);
|
|
165
|
+
console.log("Run \"ownlate workspace list\" to see available workspaces.");
|
|
166
|
+
process.exit(1);
|
|
167
|
+
}
|
|
168
|
+
await writeGlobalConfig({
|
|
169
|
+
...auth,
|
|
170
|
+
currentWorkspaceId: found.id
|
|
171
|
+
});
|
|
172
|
+
console.log(`Now using workspace: ${found.name}`);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
//#endregion
|
|
176
|
+
//#region src/commands/project.ts
|
|
177
|
+
async function projectListCommand() {
|
|
178
|
+
const auth = await requireGlobalConfig();
|
|
179
|
+
if (!auth.currentWorkspaceId) {
|
|
180
|
+
console.error("No workspace selected. Run \"ownlate workspace use <id>\" first.");
|
|
181
|
+
process.exit(1);
|
|
182
|
+
}
|
|
183
|
+
const projects = await new ApiClient(auth.apiUrl, auth.apiKey).listProjects(auth.currentWorkspaceId);
|
|
184
|
+
if (projects.length === 0) {
|
|
185
|
+
console.log("No projects found in current workspace.");
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
for (const p of projects) console.log(` ${p.name} ${p.id}`);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
//#endregion
|
|
7
192
|
//#region src/commands/init.ts
|
|
8
193
|
async function ask(rl, question, fallback = "") {
|
|
9
194
|
return (await rl.question(question)).trim() || fallback;
|
|
@@ -15,22 +200,40 @@ async function initCommand(options) {
|
|
|
15
200
|
console.error(`Config already exists: ${configPath}`);
|
|
16
201
|
process.exit(1);
|
|
17
202
|
} catch {}
|
|
203
|
+
const auth = await requireGlobalConfig();
|
|
204
|
+
const client = new ApiClient(auth.apiUrl, auth.apiKey);
|
|
18
205
|
const rl = (0, node_readline_promises.createInterface)({
|
|
19
206
|
input: process.stdin,
|
|
20
207
|
output: process.stdout
|
|
21
208
|
});
|
|
22
209
|
try {
|
|
23
|
-
console.log("Initializing ownlate config...\n");
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
210
|
+
console.log("Initializing ownlate project config...\n");
|
|
211
|
+
let projectId;
|
|
212
|
+
if (auth.currentWorkspaceId) {
|
|
213
|
+
let projects = [];
|
|
214
|
+
try {
|
|
215
|
+
projects = await client.listProjects(auth.currentWorkspaceId);
|
|
216
|
+
} catch {}
|
|
217
|
+
if (projects.length > 0) {
|
|
218
|
+
console.log("Projects in current workspace:");
|
|
219
|
+
projects.forEach((p, i) => console.log(` ${i + 1}. ${p.name} (${p.id})`));
|
|
220
|
+
console.log(` ${projects.length + 1}. Enter ID manually\n`);
|
|
221
|
+
const choice = await ask(rl, "Select project: ");
|
|
222
|
+
const idx = parseInt(choice, 10) - 1;
|
|
223
|
+
if (idx >= 0 && idx < projects.length) {
|
|
224
|
+
projectId = projects[idx].id;
|
|
225
|
+
console.log(`Using: ${projects[idx].name}`);
|
|
226
|
+
} else projectId = await ask(rl, "Project ID: ");
|
|
227
|
+
} else projectId = await ask(rl, "Project ID: ");
|
|
228
|
+
} else {
|
|
229
|
+
console.log("No workspace selected. Run \"ownlate workspace use <id>\" to pick one, or enter the project ID manually.\n");
|
|
230
|
+
projectId = await ask(rl, "Project ID: ");
|
|
231
|
+
}
|
|
27
232
|
const sourceLanguage = await ask(rl, "Source language [en]: ", "en");
|
|
28
233
|
const targetLangsRaw = await ask(rl, "Target languages, comma-separated [fr,de]: ", "fr,de");
|
|
29
234
|
const sourceFile = await ask(rl, "Source file path [locales/en.json]: ", "locales/en.json");
|
|
30
235
|
const outputPattern = await ask(rl, "Output pattern [locales/{{language}}.json]: ", "locales/{{language}}.json");
|
|
31
236
|
const config = {
|
|
32
|
-
apiUrl,
|
|
33
|
-
apiKey,
|
|
34
237
|
projectId,
|
|
35
238
|
sourceLanguage,
|
|
36
239
|
targetLanguages: targetLangsRaw.split(",").map((l) => l.trim()).filter(Boolean),
|
|
@@ -51,25 +254,16 @@ async function initCommand(options) {
|
|
|
51
254
|
|
|
52
255
|
//#endregion
|
|
53
256
|
//#region src/lib/config.ts
|
|
54
|
-
async function
|
|
257
|
+
async function readProjectConfig(configPath) {
|
|
55
258
|
const absPath = (0, node_path.resolve)(configPath);
|
|
56
259
|
let raw;
|
|
57
260
|
try {
|
|
58
261
|
raw = await (0, node_fs_promises.readFile)(absPath, "utf-8");
|
|
59
262
|
} catch {
|
|
60
|
-
throw new Error(`Config
|
|
61
|
-
}
|
|
62
|
-
let config;
|
|
63
|
-
try {
|
|
64
|
-
config = JSON.parse(raw);
|
|
65
|
-
} catch {
|
|
66
|
-
throw new Error(`Config file is not valid JSON: ${absPath}`);
|
|
263
|
+
throw new Error(`Config not found: ${absPath}\nRun "ownlate init" to create one.`);
|
|
67
264
|
}
|
|
68
|
-
config
|
|
69
|
-
config.apiKey = process.env.OWNLATE_API_KEY ?? config.apiKey;
|
|
265
|
+
const config = JSON.parse(raw);
|
|
70
266
|
for (const field of [
|
|
71
|
-
"apiUrl",
|
|
72
|
-
"apiKey",
|
|
73
267
|
"projectId",
|
|
74
268
|
"sourceLanguage",
|
|
75
269
|
"targetLanguages",
|
|
@@ -77,52 +271,10 @@ async function readConfig(configPath) {
|
|
|
77
271
|
]) if (!config[field]) throw new Error(`Missing required config field: "${field}"`);
|
|
78
272
|
return config;
|
|
79
273
|
}
|
|
80
|
-
async function
|
|
274
|
+
async function writeProjectConfig(configPath, config) {
|
|
81
275
|
await (0, node_fs_promises.writeFile)((0, node_path.resolve)(configPath), JSON.stringify(config, null, 2) + "\n");
|
|
82
276
|
}
|
|
83
277
|
|
|
84
|
-
//#endregion
|
|
85
|
-
//#region src/lib/api-client.ts
|
|
86
|
-
var ApiClient = class {
|
|
87
|
-
constructor(baseUrl, apiKey) {
|
|
88
|
-
this.baseUrl = baseUrl.replace(/\/$/, "");
|
|
89
|
-
this.apiKey = apiKey;
|
|
90
|
-
}
|
|
91
|
-
authHeaders() {
|
|
92
|
-
return { Authorization: `Bearer ${this.apiKey}` };
|
|
93
|
-
}
|
|
94
|
-
async upload(dto) {
|
|
95
|
-
const res = await fetch(`${this.baseUrl}/v1/translation-files/upload`, {
|
|
96
|
-
method: "POST",
|
|
97
|
-
headers: {
|
|
98
|
-
...this.authHeaders(),
|
|
99
|
-
"Content-Type": "application/json"
|
|
100
|
-
},
|
|
101
|
-
body: JSON.stringify(dto)
|
|
102
|
-
});
|
|
103
|
-
if (!res.ok) {
|
|
104
|
-
const text = await res.text();
|
|
105
|
-
throw new Error(`HTTP ${res.status}: ${text}`);
|
|
106
|
-
}
|
|
107
|
-
return res.json();
|
|
108
|
-
}
|
|
109
|
-
async exportTranslations(params) {
|
|
110
|
-
const query = new URLSearchParams({
|
|
111
|
-
projectId: params.projectId,
|
|
112
|
-
language: params.language,
|
|
113
|
-
format: params.format
|
|
114
|
-
});
|
|
115
|
-
if (params.fileId) query.set("fileId", params.fileId);
|
|
116
|
-
if (params.sourceLanguage) query.set("sourceLanguage", params.sourceLanguage);
|
|
117
|
-
const res = await fetch(`${this.baseUrl}/v1/segments/export?${query}`, { headers: this.authHeaders() });
|
|
118
|
-
if (!res.ok) {
|
|
119
|
-
const text = await res.text();
|
|
120
|
-
throw new Error(`HTTP ${res.status}: ${text}`);
|
|
121
|
-
}
|
|
122
|
-
return res.text();
|
|
123
|
-
}
|
|
124
|
-
};
|
|
125
|
-
|
|
126
278
|
//#endregion
|
|
127
279
|
//#region src/commands/push.ts
|
|
128
280
|
function detectFormat$1(filePath) {
|
|
@@ -133,10 +285,10 @@ function detectFormat$1(filePath) {
|
|
|
133
285
|
throw new Error(`Unsupported extension "${ext}". Use .json, .yaml, .yml or .po`);
|
|
134
286
|
}
|
|
135
287
|
async function pushCommand(options) {
|
|
136
|
-
const config = await
|
|
137
|
-
const client = new ApiClient(
|
|
288
|
+
const [auth, config] = await Promise.all([requireGlobalConfig(), readProjectConfig(options.config)]);
|
|
289
|
+
const client = new ApiClient(auth.apiUrl, auth.apiKey);
|
|
138
290
|
let configUpdated = false;
|
|
139
|
-
console.log(`Pushing to ${
|
|
291
|
+
console.log(`Pushing to ${auth.apiUrl} (project: ${config.projectId})\n`);
|
|
140
292
|
for (const file of config.files) {
|
|
141
293
|
const absPath = (0, node_path.resolve)(file.source);
|
|
142
294
|
let content;
|
|
@@ -149,13 +301,12 @@ async function pushCommand(options) {
|
|
|
149
301
|
const format = detectFormat$1(file.source);
|
|
150
302
|
const name = (0, node_path.basename)(file.source);
|
|
151
303
|
const dir = (0, node_path.dirname)(file.source);
|
|
152
|
-
const filePath = dir === "." ? "" : dir;
|
|
153
304
|
process.stdout.write(` ${file.source} ...`);
|
|
154
305
|
try {
|
|
155
306
|
const result = await client.upload({
|
|
156
307
|
projectId: config.projectId,
|
|
157
308
|
name,
|
|
158
|
-
path:
|
|
309
|
+
path: dir === "." ? "" : dir,
|
|
159
310
|
format,
|
|
160
311
|
content,
|
|
161
312
|
fileId: file.fileId
|
|
@@ -171,7 +322,7 @@ async function pushCommand(options) {
|
|
|
171
322
|
}
|
|
172
323
|
}
|
|
173
324
|
if (configUpdated) {
|
|
174
|
-
await
|
|
325
|
+
await writeProjectConfig(options.config, config);
|
|
175
326
|
console.log(`\nSaved file IDs to ${options.config}`);
|
|
176
327
|
}
|
|
177
328
|
}
|
|
@@ -186,10 +337,10 @@ function detectFormat(pattern) {
|
|
|
186
337
|
throw new Error(`Cannot detect format from output pattern "${pattern}"`);
|
|
187
338
|
}
|
|
188
339
|
async function pullCommand(options) {
|
|
189
|
-
const config = await
|
|
190
|
-
const client = new ApiClient(
|
|
340
|
+
const [auth, config] = await Promise.all([requireGlobalConfig(), readProjectConfig(options.config)]);
|
|
341
|
+
const client = new ApiClient(auth.apiUrl, auth.apiKey);
|
|
191
342
|
const languages = options.lang ? [options.lang] : config.targetLanguages;
|
|
192
|
-
console.log(`Pulling from ${
|
|
343
|
+
console.log(`Pulling from ${auth.apiUrl} (project: ${config.projectId})\n`);
|
|
193
344
|
for (const file of config.files) {
|
|
194
345
|
const format = detectFormat(file.output);
|
|
195
346
|
for (const language of languages) {
|
|
@@ -201,7 +352,8 @@ async function pullCommand(options) {
|
|
|
201
352
|
projectId: config.projectId,
|
|
202
353
|
language,
|
|
203
354
|
format,
|
|
204
|
-
fileId: file.fileId
|
|
355
|
+
fileId: file.fileId,
|
|
356
|
+
sourceLanguage: config.sourceLanguage
|
|
205
357
|
});
|
|
206
358
|
await (0, node_fs_promises.mkdir)((0, node_path.dirname)(absPath), { recursive: true });
|
|
207
359
|
await (0, node_fs_promises.writeFile)(absPath, content);
|
|
@@ -217,13 +369,19 @@ async function pullCommand(options) {
|
|
|
217
369
|
//#endregion
|
|
218
370
|
//#region src/index.ts
|
|
219
371
|
const program = new commander.Command();
|
|
220
|
-
program.name("ownlate").description("Sync translation files with your ownlate project").version("1.0.
|
|
372
|
+
program.name("ownlate").description("Sync translation files with your ownlate project").version("1.0.1");
|
|
373
|
+
program.command("login").description("Log in with your API key").option("--url <url>", "API URL (default: https://api.ownlate.com)").action(loginCommand);
|
|
374
|
+
program.command("logout").description("Remove stored credentials").action(logoutCommand);
|
|
375
|
+
const workspace = program.command("workspace").description("Manage workspaces");
|
|
376
|
+
workspace.command("list").description("List available workspaces").action(workspaceListCommand);
|
|
377
|
+
workspace.command("use <id>").description("Switch current workspace by ID or name").action(workspaceUseCommand);
|
|
378
|
+
program.command("project").description("Manage projects").command("list").description("List projects in the current workspace").action(projectListCommand);
|
|
221
379
|
const configOpt = [
|
|
222
380
|
"-c, --config <path>",
|
|
223
|
-
"path to config file",
|
|
381
|
+
"path to project config file",
|
|
224
382
|
".ownlate.json"
|
|
225
383
|
];
|
|
226
|
-
program.command("init").description("Create .ownlate.json
|
|
384
|
+
program.command("init").description("Create .ownlate.json for this project").option(...configOpt).action(initCommand);
|
|
227
385
|
program.command("push").description("Upload source files — creates/updates segments in ownlate").option(...configOpt).action(pushCommand);
|
|
228
386
|
program.command("pull").description("Download translated files from ownlate to local filesystem").option(...configOpt).option("-l, --lang <code>", "pull a single language only (e.g. fr)").action(pullCommand);
|
|
229
387
|
program.parseAsync(process.argv).catch((err) => {
|
package/package.json
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ownlate/cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "Ownlate CLI — push source files and pull translations",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
7
7
|
"dist"
|
|
8
8
|
],
|
|
9
9
|
"bin": {
|
|
10
|
-
"ownlate": "./dist/index.cjs"
|
|
10
|
+
"ownlate": "./dist/index.cjs",
|
|
11
|
+
"cli": "./dist/index.cjs"
|
|
11
12
|
},
|
|
12
13
|
"engines": {
|
|
13
14
|
"node": ">=18.0.0"
|
|
@@ -35,4 +36,4 @@
|
|
|
35
36
|
"tsdown": "catalog:dev",
|
|
36
37
|
"typescript": "catalog:dev"
|
|
37
38
|
}
|
|
38
|
-
}
|
|
39
|
+
}
|