langctl 0.1.2 → 0.3.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.
Files changed (63) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/LICENSE +1 -1
  3. package/README.md +159 -1151
  4. package/dist/commands/auth.js +82 -24
  5. package/dist/commands/config.js +36 -25
  6. package/dist/commands/export.js +11 -112
  7. package/dist/commands/import.js +32 -164
  8. package/dist/commands/init.js +59 -80
  9. package/dist/commands/keys.js +145 -381
  10. package/dist/commands/org.js +45 -140
  11. package/dist/commands/projects.js +125 -390
  12. package/dist/commands/pull.js +78 -183
  13. package/dist/commands/push.js +125 -0
  14. package/dist/commands/team.js +68 -272
  15. package/dist/core/config.js +130 -0
  16. package/dist/core/errors.js +67 -0
  17. package/dist/core/files.js +38 -0
  18. package/dist/core/http.js +121 -0
  19. package/dist/core/output.js +96 -0
  20. package/dist/core/project.js +58 -0
  21. package/dist/core/prompts.js +56 -0
  22. package/dist/formats/index.js +285 -0
  23. package/dist/index.js +261 -508
  24. package/dist/version.js +4 -0
  25. package/package.json +22 -16
  26. package/.gitattributes +0 -2
  27. package/NEXT_PHASE.md +0 -618
  28. package/WORKING_INSTRUCTIONS.md +0 -1389
  29. package/dist/auth.d.ts +0 -37
  30. package/dist/auth.js +0 -112
  31. package/dist/commands/auth.d.ts +0 -3
  32. package/dist/commands/config.d.ts +0 -2
  33. package/dist/commands/debug.d.ts +0 -3
  34. package/dist/commands/debug.js +0 -57
  35. package/dist/commands/export.d.ts +0 -14
  36. package/dist/commands/import.d.ts +0 -11
  37. package/dist/commands/init.d.ts +0 -2
  38. package/dist/commands/keys.d.ts +0 -25
  39. package/dist/commands/org.d.ts +0 -13
  40. package/dist/commands/projects.d.ts +0 -30
  41. package/dist/commands/pull.d.ts +0 -9
  42. package/dist/commands/team.d.ts +0 -29
  43. package/dist/config.d.ts +0 -47
  44. package/dist/config.js +0 -69
  45. package/dist/exporters/index.d.ts +0 -36
  46. package/dist/exporters/index.js +0 -214
  47. package/dist/index.d.ts +0 -3
  48. package/dist/supabase.d.ts +0 -14
  49. package/dist/supabase.js +0 -30
  50. package/dist/utils/banner.d.ts +0 -9
  51. package/dist/utils/banner.js +0 -24
  52. package/translations/en.json +0 -5
  53. package/translations/en.xml +0 -9
  54. package/translations/fr.json +0 -4
  55. package/translations/fr.xml +0 -7
  56. package/translations/hi.json +0 -4
  57. package/translations/hi.xml +0 -7
  58. package/translations/ios/en.strings +0 -9
  59. package/translations/ios/fr.strings +0 -6
  60. package/translations/ios/hi.strings +0 -6
  61. package/translations/ios/ja.strings +0 -6
  62. package/translations/ja.json +0 -4
  63. package/translations/ja.xml +0 -7
@@ -1,396 +1,160 @@
1
1
  import chalk from 'chalk';
2
- import ora from 'ora';
3
- import { isAuthenticated, getApiKey } from '../auth.js';
4
- const EDGE_FUNCTION_URL = 'https://bcgnmvkgkbhbxzzflwdb.supabase.co/functions/v1/manage-translation-keys';
5
- const LIST_PROJECTS_URL = 'https://bcgnmvkgkbhbxzzflwdb.supabase.co/functions/v1/list-projects';
6
- /**
7
- * Get project by slug
8
- */
9
- async function getProjectBySlug(apiKey, slug) {
2
+ import { CliError, ExitCode, usageError } from '../core/errors.js';
3
+ import { getSession } from '../core/http.js';
4
+ import { log, printJson, runtime, spinner, table } from '../core/output.js';
5
+ import { confirm } from '../core/prompts.js';
6
+ import { assertLanguages, getProject, listAllKeys, projectSlugFrom, requireKey } from '../core/project.js';
7
+ export async function listKeysCommand(projectArg, opts) {
8
+ const session = await getSession();
9
+ const project = await getProject(session, projectSlugFrom(projectArg));
10
+ const spin = spinner('Fetching keys…');
11
+ let keys;
10
12
  try {
11
- const response = await fetch(LIST_PROJECTS_URL, {
12
- method: 'POST',
13
- headers: {
14
- 'Content-Type': 'application/json',
15
- 'X-API-Key': apiKey
16
- }
13
+ keys = await listAllKeys(session, project, {
14
+ module: opts.module,
15
+ search: opts.search,
16
+ published: opts.published ? true : opts.drafts ? false : undefined,
17
17
  });
18
- if (!response.ok)
19
- return null;
20
- const data = await response.json();
21
- if (!data.success || !data.projects)
22
- return null;
23
- return data.projects.find((p) => p.slug === slug) || null;
24
18
  }
25
- catch (error) {
26
- return null;
19
+ finally {
20
+ spin.stop();
27
21
  }
28
- }
29
- /**
30
- * List translation keys
31
- */
32
- export async function listKeysCommand(projectSlug, options) {
33
- if (!isAuthenticated()) {
34
- console.log(chalk.red('✗ Not authenticated. Please run "langctl auth <api-key>" first.\n'));
35
- return;
36
- }
37
- const apiKey = getApiKey();
38
- const spinner = ora('Fetching keys...').start();
39
- try {
40
- const project = await getProjectBySlug(apiKey, projectSlug);
41
- if (!project) {
42
- spinner.fail(chalk.red(`Project "${projectSlug}" not found`));
43
- return;
44
- }
45
- const response = await fetch(EDGE_FUNCTION_URL, {
46
- method: 'POST',
47
- headers: {
48
- 'Content-Type': 'application/json',
49
- 'X-API-Key': apiKey
50
- },
51
- body: JSON.stringify({
52
- action: 'list',
53
- projectId: project.id,
54
- module: options.module,
55
- published: options.published,
56
- search: options.search,
57
- limit: options.limit || 100,
58
- offset: options.offset || 0
59
- })
60
- });
61
- const data = await response.json();
62
- if (!data.success) {
63
- throw new Error(data.error);
64
- }
65
- spinner.stop();
66
- if (!data.keys || data.keys.length === 0) {
67
- console.log(chalk.yellow('\nNo keys found\n'));
68
- return;
69
- }
70
- console.log(chalk.blue.bold(`\n🔑 Translation Keys (${data.keys.length}${data.total ? ` of ${data.total}` : ''})\n`));
71
- data.keys.forEach((key) => {
72
- console.log(chalk.white.bold(key.key));
73
- if (key.description) {
74
- console.log(chalk.gray(` Description: ${key.description}`));
75
- }
76
- if (key.module) {
77
- console.log(chalk.gray(` Module: ${key.module}`));
78
- }
79
- console.log(chalk.gray(` Published: ${key.published ? chalk.green('Yes') : chalk.yellow('No')}`));
80
- const langs = Object.keys(key.translations);
81
- if (langs.length > 0) {
82
- console.log(chalk.gray(` Languages: ${langs.join(', ')}`));
83
- }
84
- console.log('');
85
- });
22
+ const limit = opts.limit ? Number(opts.limit) : undefined;
23
+ if (limit !== undefined && (!Number.isInteger(limit) || limit < 1))
24
+ throw usageError('--limit must be a positive integer');
25
+ const shown = limit ? keys.slice(0, limit) : keys;
26
+ if (runtime.json) {
27
+ return printJson(shown.map(k => ({
28
+ key: k.key, published: k.published, module: k.module ?? null, description: k.description ?? null, translations: k.translations,
29
+ })));
86
30
  }
87
- catch (error) {
88
- spinner.fail(chalk.red('Failed to list keys'));
89
- console.error(chalk.red(`Error: ${error.message}\n`));
90
- }
91
- }
92
- /**
93
- * Get single key
94
- */
95
- export async function getKeyCommand(projectSlug, keyName) {
96
- if (!isAuthenticated()) {
97
- console.log(chalk.red('✗ Not authenticated. Please run "langctl auth <api-key>" first.\n'));
31
+ if (shown.length === 0) {
32
+ log.info('No keys match.');
98
33
  return;
99
34
  }
100
- const apiKey = getApiKey();
101
- const spinner = ora('Fetching key...').start();
102
- try {
103
- const project = await getProjectBySlug(apiKey, projectSlug);
104
- if (!project) {
105
- spinner.fail(chalk.red(`Project "${projectSlug}" not found`));
106
- return;
107
- }
108
- // First list to find the key ID
109
- const listResponse = await fetch(EDGE_FUNCTION_URL, {
110
- method: 'POST',
111
- headers: {
112
- 'Content-Type': 'application/json',
113
- 'X-API-Key': apiKey
114
- },
115
- body: JSON.stringify({
116
- action: 'list',
117
- projectId: project.id,
118
- search: keyName,
119
- limit: 1
120
- })
121
- });
122
- const listData = await listResponse.json();
123
- if (!listData.success || !listData.keys || listData.keys.length === 0) {
124
- spinner.fail(chalk.red(`Key "${keyName}" not found`));
125
- return;
126
- }
127
- const key = listData.keys.find((k) => k.key === keyName);
128
- if (!key) {
129
- spinner.fail(chalk.red(`Key "${keyName}" not found`));
130
- return;
131
- }
132
- spinner.stop();
133
- console.log(chalk.blue.bold('\n🔑 Translation Key Details\n'));
134
- console.log(chalk.white.bold(`Key: ${key.key}`));
135
- console.log(chalk.gray(`ID: ${key.id}`));
136
- if (key.description) {
137
- console.log(chalk.gray(`Description: ${key.description}`));
138
- }
139
- if (key.module) {
140
- console.log(chalk.gray(`Module: ${key.module}`));
141
- }
142
- console.log(chalk.gray(`Published: ${key.published ? chalk.green('Yes') : chalk.yellow('No')}`));
143
- console.log(chalk.blue.bold('\nTranslations:'));
144
- Object.entries(key.translations).forEach(([lang, value]) => {
145
- console.log(chalk.white(` ${lang}: ${value}`));
146
- });
147
- console.log('');
148
- }
149
- catch (error) {
150
- spinner.fail(chalk.red('Failed to get key'));
151
- console.error(chalk.red(`Error: ${error.message}\n`));
152
- }
35
+ table(shown.map(k => [
36
+ k.key,
37
+ k.published ? chalk.green('published') : chalk.yellow('draft'),
38
+ k.module ?? '',
39
+ `${project.languages.filter(l => k.translations?.[l]).length}/${project.languages.length}`,
40
+ ]), ['KEY', 'STATUS', 'MODULE', 'LANGS']);
41
+ if (limit && keys.length > limit)
42
+ log.info(chalk.dim(`Showing ${limit} of ${keys.length}. Use --json for scripting.`));
153
43
  }
154
- /**
155
- * Create translation key
156
- */
157
- export async function createKeyCommand(projectSlug, keyName, options) {
158
- if (!isAuthenticated()) {
159
- console.log(chalk.red('✗ Not authenticated. Please run "langctl auth <api-key>" first.\n'));
160
- return;
161
- }
162
- const apiKey = getApiKey();
163
- const spinner = ora('Creating key...').start();
164
- try {
165
- const project = await getProjectBySlug(apiKey, projectSlug);
166
- if (!project) {
167
- spinner.fail(chalk.red(`Project "${projectSlug}" not found`));
168
- return;
169
- }
170
- // Build translations object from --value-* options
171
- const translations = {};
172
- Object.keys(options).forEach(opt => {
173
- if (opt.startsWith('value')) {
174
- const lang = opt.replace('value', '').toLowerCase();
175
- if (lang) {
176
- translations[lang] = options[opt];
177
- }
178
- }
179
- });
180
- const response = await fetch(EDGE_FUNCTION_URL, {
181
- method: 'POST',
182
- headers: {
183
- 'Content-Type': 'application/json',
184
- 'X-API-Key': apiKey
185
- },
186
- body: JSON.stringify({
187
- action: 'create',
188
- projectId: project.id,
189
- key: keyName,
190
- translations,
191
- description: options.description,
192
- module: options.module,
193
- tags: options.tags ? options.tags.split(',') : []
194
- })
195
- });
196
- const data = await response.json();
197
- if (!data.success) {
198
- throw new Error(data.error);
199
- }
200
- spinner.succeed(chalk.green(`Created key: ${keyName}`));
201
- console.log('');
202
- }
203
- catch (error) {
204
- spinner.fail(chalk.red('Failed to create key'));
205
- console.error(chalk.red(`Error: ${error.message}\n`));
44
+ export async function getKeyCommand(projectArg, keyName) {
45
+ const session = await getSession();
46
+ const project = await getProject(session, projectArg);
47
+ const key = await requireKey(session, project, keyName);
48
+ if (runtime.json)
49
+ return printJson(key);
50
+ log.out(chalk.bold(key.key) + ' ' + (key.published ? chalk.green('published') : chalk.yellow('draft')));
51
+ if (key.description)
52
+ log.out(chalk.dim(key.description));
53
+ if (key.module)
54
+ log.out(chalk.dim(`module: ${key.module}`));
55
+ for (const lang of project.languages) {
56
+ const v = key.translations?.[lang];
57
+ log.out(` ${lang.padEnd(6)} ${v === undefined || v === '' ? chalk.dim('— missing') : v}`);
206
58
  }
207
59
  }
208
- /**
209
- * Delete translation key
210
- */
211
- export async function deleteKeyCommand(projectSlug, keyName) {
212
- if (!isAuthenticated()) {
213
- console.log(chalk.red('✗ Not authenticated. Please run "langctl auth <api-key>" first.\n'));
214
- return;
215
- }
216
- const apiKey = getApiKey();
217
- const spinner = ora('Deleting key...').start();
218
- try {
219
- const project = await getProjectBySlug(apiKey, projectSlug);
220
- if (!project) {
221
- spinner.fail(chalk.red(`Project "${projectSlug}" not found`));
222
- return;
223
- }
224
- // Find key ID
225
- const listResponse = await fetch(EDGE_FUNCTION_URL, {
226
- method: 'POST',
227
- headers: {
228
- 'Content-Type': 'application/json',
229
- 'X-API-Key': apiKey
230
- },
231
- body: JSON.stringify({
232
- action: 'list',
233
- projectId: project.id,
234
- search: keyName,
235
- limit: 1
236
- })
237
- });
238
- const listData = await listResponse.json();
239
- const key = listData.keys?.find((k) => k.key === keyName);
240
- if (!key) {
241
- spinner.fail(chalk.red(`Key "${keyName}" not found`));
242
- return;
243
- }
244
- // Delete key
245
- const response = await fetch(EDGE_FUNCTION_URL, {
246
- method: 'POST',
247
- headers: {
248
- 'Content-Type': 'application/json',
249
- 'X-API-Key': apiKey
250
- },
251
- body: JSON.stringify({
252
- action: 'delete',
253
- projectId: project.id,
254
- keyId: key.id
255
- })
256
- });
257
- const data = await response.json();
258
- if (!data.success) {
259
- throw new Error(data.error);
260
- }
261
- spinner.succeed(chalk.green(`Deleted key: ${keyName}`));
262
- console.log('');
263
- }
264
- catch (error) {
265
- spinner.fail(chalk.red('Failed to delete key'));
266
- console.error(chalk.red(`Error: ${error.message}\n`));
267
- }
60
+ /** Collect `--value en=Hello --value es=Hola` plus the legacy `--value-en` style flags. */
61
+ function collectValues(opts) {
62
+ const out = {};
63
+ for (const pair of opts.value ?? []) {
64
+ const i = pair.indexOf('=');
65
+ if (i < 1)
66
+ throw usageError(`--value expects LANG=TEXT, got "${pair}"`, 'Example: --value en="Welcome" --value es="Bienvenido"');
67
+ out[pair.slice(0, i).trim()] = pair.slice(i + 1);
68
+ }
69
+ for (const lang of ['en', 'es', 'fr', 'de']) {
70
+ const legacy = opts[`value${lang[0].toUpperCase()}${lang[1]}`];
71
+ if (typeof legacy === 'string')
72
+ out[lang] = legacy;
73
+ }
74
+ return out;
268
75
  }
269
- /**
270
- * Update single translation
271
- */
272
- export async function translateKeyCommand(projectSlug, keyName, options) {
273
- if (!isAuthenticated()) {
274
- console.log(chalk.red('✗ Not authenticated. Please run "langctl auth <api-key>" first.\n'));
275
- return;
276
- }
277
- const apiKey = getApiKey();
278
- const spinner = ora('Updating translation...').start();
279
- try {
280
- const project = await getProjectBySlug(apiKey, projectSlug);
281
- if (!project) {
282
- spinner.fail(chalk.red(`Project "${projectSlug}" not found`));
283
- return;
284
- }
285
- // Find key ID
286
- const listResponse = await fetch(EDGE_FUNCTION_URL, {
287
- method: 'POST',
288
- headers: {
289
- 'Content-Type': 'application/json',
290
- 'X-API-Key': apiKey
291
- },
292
- body: JSON.stringify({
293
- action: 'list',
294
- projectId: project.id,
295
- search: keyName,
296
- limit: 1
297
- })
298
- });
299
- const listData = await listResponse.json();
300
- const key = listData.keys?.find((k) => k.key === keyName);
301
- if (!key) {
302
- spinner.fail(chalk.red(`Key "${keyName}" not found`));
303
- return;
304
- }
305
- // Update translation
306
- const response = await fetch(EDGE_FUNCTION_URL, {
307
- method: 'POST',
308
- headers: {
309
- 'Content-Type': 'application/json',
310
- 'X-API-Key': apiKey
311
- },
312
- body: JSON.stringify({
313
- action: 'translate',
314
- projectId: project.id,
315
- keyId: key.id,
316
- language: options.language,
317
- value: options.value
318
- })
319
- });
320
- const data = await response.json();
321
- if (!data.success) {
322
- throw new Error(data.error);
323
- }
324
- spinner.succeed(chalk.green(`Updated ${options.language} translation for ${keyName}`));
325
- console.log('');
326
- }
327
- catch (error) {
328
- spinner.fail(chalk.red('Failed to update translation'));
329
- console.error(chalk.red(`Error: ${error.message}\n`));
330
- }
76
+ export async function createKeyCommand(projectArg, keyName, opts) {
77
+ const session = await getSession();
78
+ const project = await getProject(session, projectArg);
79
+ const translations = collectValues(opts);
80
+ assertLanguages(project, Object.keys(translations));
81
+ const key = await session.api.post(`/orgs/${session.orgId}/projects/${project.id}/keys`, {
82
+ key: keyName,
83
+ translations,
84
+ description: opts.description,
85
+ module: opts.module,
86
+ tags: typeof opts.tags === 'string' ? opts.tags.split(',').map(t => t.trim()).filter(Boolean) : undefined,
87
+ published: Boolean(opts.publish),
88
+ });
89
+ if (runtime.json)
90
+ return printJson(key);
91
+ log.success(`Created ${chalk.bold(keyName)}${opts.publish ? ' (published)' : ' (draft — publish with "langctl keys publish")'}`);
331
92
  }
332
- /**
333
- * Publish/unpublish keys
334
- */
335
- export async function publishKeysCommand(projectSlug, keys, options) {
336
- if (!isAuthenticated()) {
337
- console.log(chalk.red('✗ Not authenticated. Please run "langctl auth <api-key>" first.\n'));
338
- return;
339
- }
340
- const apiKey = getApiKey();
341
- const action = options.unpublish ? 'unpublish' : 'publish';
342
- const spinner = ora(`${action === 'publish' ? 'Publishing' : 'Unpublishing'} keys...`).start();
343
- try {
344
- const project = await getProjectBySlug(apiKey, projectSlug);
345
- if (!project) {
346
- spinner.fail(chalk.red(`Project "${projectSlug}" not found`));
347
- return;
348
- }
349
- // Get key IDs
350
- const listResponse = await fetch(EDGE_FUNCTION_URL, {
351
- method: 'POST',
352
- headers: {
353
- 'Content-Type': 'application/json',
354
- 'X-API-Key': apiKey
355
- },
356
- body: JSON.stringify({
357
- action: 'list',
358
- projectId: project.id,
359
- limit: 1000
360
- })
361
- });
362
- const listData = await listResponse.json();
363
- const keyIds = listData.keys
364
- ?.filter((k) => keys.includes(k.key))
365
- .map((k) => k.id) || [];
366
- if (keyIds.length === 0) {
367
- spinner.fail(chalk.red('No matching keys found'));
368
- return;
369
- }
370
- // Publish/unpublish keys
371
- const response = await fetch(EDGE_FUNCTION_URL, {
372
- method: 'POST',
373
- headers: {
374
- 'Content-Type': 'application/json',
375
- 'X-API-Key': apiKey
376
- },
377
- body: JSON.stringify({
378
- action: 'publish',
379
- projectId: project.id,
380
- keyIds,
381
- published: !options.unpublish
382
- })
383
- });
384
- const data = await response.json();
385
- if (!data.success) {
386
- throw new Error(data.error);
387
- }
388
- spinner.succeed(chalk.green(data.message));
389
- console.log('');
390
- }
391
- catch (error) {
392
- spinner.fail(chalk.red(`Failed to ${action} keys`));
393
- console.error(chalk.red(`Error: ${error.message}\n`));
93
+ export async function updateKeyCommand(projectArg, keyName, opts) {
94
+ const session = await getSession();
95
+ const project = await getProject(session, projectArg);
96
+ const key = await requireKey(session, project, keyName);
97
+ const translations = collectValues(opts);
98
+ // legacy: keys translate <project> <key> -l es -v "Hola"
99
+ if (typeof opts.language === 'string') {
100
+ if (typeof opts.text !== 'string')
101
+ throw usageError('--language needs --text (or use --value LANG=TEXT)');
102
+ translations[opts.language] = opts.text;
103
+ }
104
+ assertLanguages(project, Object.keys(translations));
105
+ const body = {};
106
+ if (Object.keys(translations).length)
107
+ body.translations = { ...key.translations, ...translations };
108
+ if (opts.description !== undefined)
109
+ body.description = opts.description;
110
+ if (opts.module !== undefined)
111
+ body.module = opts.module;
112
+ if (Object.keys(body).length === 0)
113
+ throw usageError('Nothing to update.', 'Pass --value LANG=TEXT, --description or --module.');
114
+ const updated = await session.api.patch(`/orgs/${session.orgId}/projects/${project.id}/keys/${key.id}`, body);
115
+ if (runtime.json)
116
+ return printJson(updated);
117
+ log.success(`Updated ${chalk.bold(keyName)}${Object.keys(translations).length ? ` (${Object.keys(translations).join(', ')})` : ''}`);
118
+ }
119
+ export async function deleteKeyCommand(projectArg, keyNames) {
120
+ const session = await getSession();
121
+ const project = await getProject(session, projectArg);
122
+ const keys = [];
123
+ for (const name of keyNames)
124
+ keys.push(await requireKey(session, project, name));
125
+ if (!(await confirm(`Delete ${keys.length === 1 ? `key "${keys[0].key}"` : `${keys.length} keys`} from ${project.slug}?`))) {
126
+ throw new CliError('Cancelled.', ExitCode.Error);
127
+ }
128
+ for (const k of keys)
129
+ await session.api.delete(`/orgs/${session.orgId}/projects/${project.id}/keys/${k.id}`);
130
+ if (runtime.json)
131
+ return printJson({ deleted: keys.map(k => k.key) });
132
+ log.success(`Deleted ${keys.map(k => chalk.bold(k.key)).join(', ')}`);
133
+ }
134
+ export async function publishKeysCommand(projectArg, keyNames, opts, publish = true) {
135
+ const published = publish && !opts.unpublish;
136
+ const session = await getSession();
137
+ const project = await getProject(session, projectSlugFrom(projectArg));
138
+ let names = keyNames;
139
+ if (opts.all || opts.module) {
140
+ if (names.length)
141
+ throw usageError('Pass key names or --all/--module, not both.');
142
+ names = (await listAllKeys(session, project, { module: opts.module, published: !published })).map(k => k.key);
143
+ }
144
+ else if (names.length === 0) {
145
+ throw usageError('No keys given.', `Example: langctl keys ${published ? 'publish' : 'unpublish'} ${project.slug} home.title home.subtitle (or --all / --module <name>)`);
146
+ }
147
+ if (names.length === 0) {
148
+ if (runtime.json)
149
+ return printJson({ count: 0, notFound: [] });
150
+ return log.info(`Nothing to ${published ? 'publish' : 'unpublish'}.`);
151
+ }
152
+ const res = await session.api.post(`/orgs/${session.orgId}/projects/${project.id}/keys/bulk-publish`, { keys: names, published }, true);
153
+ if (runtime.json)
154
+ printJson(res);
155
+ else
156
+ log.success(`${published ? 'Published' : 'Unpublished'} ${res.count} key(s)`);
157
+ if (res.notFound?.length) {
158
+ throw new CliError(`Not found: ${res.notFound.join(', ')}`, ExitCode.NotFound);
394
159
  }
395
160
  }
396
- //# sourceMappingURL=keys.js.map