langctl 0.1.1 → 0.2.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.
@@ -1,54 +1,325 @@
1
1
  import chalk from 'chalk';
2
2
  import ora from 'ora';
3
- import { getSupabase } from '../supabase.js';
4
- import { isAuthenticated, getOrganizationId } from '../auth.js';
3
+ import { isAuthenticated } from '../auth.js';
4
+ import { getApiClient } from '../api.js';
5
+ import { config } from '../config.js';
6
+ /**
7
+ * Resolve a project slug to a full project object
8
+ */
9
+ async function resolveProject(orgId, slug) {
10
+ const api = getApiClient();
11
+ return api.get(`/orgs/${orgId}/projects/by-slug/${slug}`);
12
+ }
13
+ /**
14
+ * List all projects
15
+ */
5
16
  export async function projectsListCommand() {
6
- // Check authentication
7
17
  if (!isAuthenticated()) {
8
18
  console.log(chalk.red('✗ Not authenticated. Please run "langctl auth <api-key>" first.\n'));
19
+ process.exitCode = 1;
9
20
  return;
10
21
  }
11
- const organizationId = getOrganizationId();
12
- if (!organizationId) {
13
- console.log(chalk.red('✗ Organization ID not found. Please run "langctl init" again.\n'));
22
+ const orgId = config.get('organizationId');
23
+ if (!orgId) {
24
+ console.log(chalk.red('✗ Organization ID not found. Please run "langctl auth <api-key>" again.\n'));
25
+ process.exitCode = 1;
14
26
  return;
15
27
  }
16
28
  const spinner = ora('Fetching projects...').start();
17
29
  try {
18
- const supabase = getSupabase();
19
- const { data, error } = await supabase
20
- .from('projects')
21
- .select('*')
22
- .eq('organization_id', organizationId)
23
- .is('deleted_at', null)
24
- .order('created_at', { ascending: false });
25
- if (error) {
26
- spinner.fail(chalk.red('Failed to fetch projects'));
27
- console.error(chalk.red(`Error: ${error.message}\n`));
28
- return;
29
- }
30
+ const api = getApiClient();
31
+ const projects = await api.get(`/orgs/${orgId}/projects`);
30
32
  spinner.stop();
31
- if (!data || data.length === 0) {
33
+ if (!projects || projects.length === 0) {
32
34
  console.log(chalk.yellow('\nNo projects found. Create one in the dashboard first.\n'));
33
35
  return;
34
36
  }
35
- console.log(chalk.blue.bold(`\n📦 Projects (${data.length})\n`));
36
- data.forEach((project) => {
37
+ console.log(chalk.blue.bold(`\n📦 Projects (${projects.length})\n`));
38
+ projects.forEach((project) => {
37
39
  console.log(chalk.white.bold(project.name));
38
40
  console.log(chalk.gray(` Slug: ${project.slug}`));
39
41
  if (project.description) {
40
42
  console.log(chalk.gray(` Description: ${project.description}`));
41
43
  }
42
44
  console.log(chalk.gray(` Languages: ${project.languages.join(', ')}`));
43
- console.log(chalk.gray(` Default: ${project.default_language}`));
45
+ console.log(chalk.gray(` Default: ${project.defaultLanguage}`));
46
+ if (project.modules && project.modules.length > 0) {
47
+ console.log(chalk.gray(` Modules: ${project.modules.join(', ')}`));
48
+ }
44
49
  console.log('');
45
50
  });
46
- console.log(chalk.blue('To pull translations, run:'));
47
- console.log(chalk.white(` langctl pull ${data[0].slug}\n`));
51
+ console.log(chalk.blue('To export translations, run:'));
52
+ console.log(chalk.white(` langctl export ${projects[0].slug} -l en\n`));
48
53
  }
49
54
  catch (error) {
50
55
  spinner.fail(chalk.red('Failed to fetch projects'));
51
56
  console.error(chalk.red(`Error: ${error.message}\n`));
57
+ process.exitCode = 1;
58
+ }
59
+ }
60
+ /**
61
+ * Create new project
62
+ */
63
+ export async function projectsCreateCommand(name, options) {
64
+ if (!isAuthenticated()) {
65
+ console.log(chalk.red('✗ Not authenticated. Please run "langctl auth <api-key>" first.\n'));
66
+ process.exitCode = 1;
67
+ return;
68
+ }
69
+ const orgId = config.get('organizationId');
70
+ if (!orgId) {
71
+ console.log(chalk.red('✗ Organization ID not found. Please run "langctl auth <api-key>" again.\n'));
72
+ process.exitCode = 1;
73
+ return;
74
+ }
75
+ const spinner = ora('Creating project...').start();
76
+ try {
77
+ const api = getApiClient();
78
+ const languages = options.languages ? options.languages.split(',') : ['en'];
79
+ const defaultLanguage = options.defaultLanguage || languages[0];
80
+ const project = await api.post(`/orgs/${orgId}/projects`, {
81
+ name,
82
+ description: options.description,
83
+ languages,
84
+ defaultLanguage
85
+ });
86
+ spinner.succeed(chalk.green(`Created project: ${name} (${project.slug})`));
87
+ console.log('');
88
+ }
89
+ catch (error) {
90
+ spinner.fail(chalk.red('Failed to create project'));
91
+ console.error(chalk.red(`Error: ${error.message}\n`));
92
+ process.exitCode = 1;
93
+ }
94
+ }
95
+ /**
96
+ * Get project details
97
+ */
98
+ export async function projectsGetCommand(slug) {
99
+ if (!isAuthenticated()) {
100
+ console.log(chalk.red('✗ Not authenticated. Please run "langctl auth <api-key>" first.\n'));
101
+ process.exitCode = 1;
102
+ return;
103
+ }
104
+ const orgId = config.get('organizationId');
105
+ if (!orgId) {
106
+ console.log(chalk.red('✗ Organization ID not found. Please run "langctl auth <api-key>" again.\n'));
107
+ process.exitCode = 1;
108
+ return;
109
+ }
110
+ const spinner = ora('Fetching project...').start();
111
+ try {
112
+ const project = await resolveProject(orgId, slug);
113
+ spinner.stop();
114
+ console.log(chalk.blue.bold('\n📦 Project Details\n'));
115
+ console.log(chalk.white.bold(project.name));
116
+ console.log(chalk.gray(`Slug: ${project.slug}`));
117
+ console.log(chalk.gray(`ID: ${project.id}`));
118
+ if (project.description) {
119
+ console.log(chalk.gray(`Description: ${project.description}`));
120
+ }
121
+ console.log(chalk.gray(`Languages: ${project.languages.join(', ')}`));
122
+ console.log(chalk.gray(`Default Language: ${project.defaultLanguage}`));
123
+ if (project.modules && project.modules.length > 0) {
124
+ console.log(chalk.gray(`Modules: ${project.modules.join(', ')}`));
125
+ }
126
+ console.log('');
127
+ }
128
+ catch (error) {
129
+ spinner.fail(chalk.red(`Failed to fetch project "${slug}"`));
130
+ console.error(chalk.red(`Error: ${error.message}\n`));
131
+ process.exitCode = 1;
132
+ }
133
+ }
134
+ /**
135
+ * Update project
136
+ */
137
+ export async function projectsUpdateCommand(slug, options) {
138
+ if (!isAuthenticated()) {
139
+ console.log(chalk.red('✗ Not authenticated. Please run "langctl auth <api-key>" first.\n'));
140
+ process.exitCode = 1;
141
+ return;
142
+ }
143
+ const orgId = config.get('organizationId');
144
+ if (!orgId) {
145
+ console.log(chalk.red('✗ Organization ID not found. Please run "langctl auth <api-key>" again.\n'));
146
+ process.exitCode = 1;
147
+ return;
148
+ }
149
+ const spinner = ora('Updating project...').start();
150
+ try {
151
+ const project = await resolveProject(orgId, slug);
152
+ const api = getApiClient();
153
+ const updateData = {};
154
+ if (options.name)
155
+ updateData.name = options.name;
156
+ if (options.description !== undefined)
157
+ updateData.description = options.description;
158
+ if (options.defaultLanguage)
159
+ updateData.defaultLanguage = options.defaultLanguage;
160
+ // Keep support for --languages by reconciling desired list through add/remove-language APIs.
161
+ let targetLanguages = null;
162
+ if (options.languages) {
163
+ const parsedLanguages = options.languages
164
+ .split(',')
165
+ .map((lang) => lang.trim())
166
+ .filter((lang) => lang.length > 0);
167
+ if (parsedLanguages.length === 0) {
168
+ throw new Error('At least one language must be provided when using --languages');
169
+ }
170
+ const effectiveDefaultLanguage = options.defaultLanguage || project.defaultLanguage;
171
+ if (!parsedLanguages.includes(effectiveDefaultLanguage)) {
172
+ throw new Error(`Default language "${effectiveDefaultLanguage}" must be included in --languages`);
173
+ }
174
+ targetLanguages = parsedLanguages;
175
+ }
176
+ if (Object.keys(updateData).length > 0) {
177
+ await api.patch(`/orgs/${orgId}/projects/${project.id}`, updateData);
178
+ }
179
+ if (targetLanguages) {
180
+ const desiredLanguages = targetLanguages;
181
+ const currentLanguages = project.languages || [];
182
+ const toAdd = desiredLanguages.filter((lang) => !currentLanguages.includes(lang));
183
+ const toRemove = currentLanguages.filter((lang) => !desiredLanguages.includes(lang));
184
+ for (const lang of toAdd) {
185
+ await api.post(`/orgs/${orgId}/projects/${project.id}/languages`, { code: lang });
186
+ }
187
+ for (const lang of toRemove) {
188
+ await api.delete(`/orgs/${orgId}/projects/${project.id}/languages/${lang}`);
189
+ }
190
+ }
191
+ spinner.succeed(chalk.green(`Updated project: ${slug}`));
192
+ console.log('');
193
+ }
194
+ catch (error) {
195
+ spinner.fail(chalk.red('Failed to update project'));
196
+ console.error(chalk.red(`Error: ${error.message}\n`));
197
+ process.exitCode = 1;
198
+ }
199
+ }
200
+ /**
201
+ * Delete project
202
+ */
203
+ export async function projectsDeleteCommand(slug) {
204
+ if (!isAuthenticated()) {
205
+ console.log(chalk.red('✗ Not authenticated. Please run "langctl auth <api-key>" first.\n'));
206
+ process.exitCode = 1;
207
+ return;
208
+ }
209
+ const orgId = config.get('organizationId');
210
+ if (!orgId) {
211
+ console.log(chalk.red('✗ Organization ID not found. Please run "langctl auth <api-key>" again.\n'));
212
+ process.exitCode = 1;
213
+ return;
214
+ }
215
+ const spinner = ora('Deleting project...').start();
216
+ try {
217
+ const project = await resolveProject(orgId, slug);
218
+ const api = getApiClient();
219
+ await api.delete(`/orgs/${orgId}/projects/${project.id}`);
220
+ spinner.succeed(chalk.green(`Deleted project: ${slug}`));
221
+ console.log('');
222
+ }
223
+ catch (error) {
224
+ spinner.fail(chalk.red('Failed to delete project'));
225
+ console.error(chalk.red(`Error: ${error.message}\n`));
226
+ process.exitCode = 1;
227
+ }
228
+ }
229
+ /**
230
+ * Add language to project
231
+ */
232
+ export async function projectsAddLanguageCommand(slug, language) {
233
+ if (!isAuthenticated()) {
234
+ console.log(chalk.red('✗ Not authenticated. Please run "langctl auth <api-key>" first.\n'));
235
+ process.exitCode = 1;
236
+ return;
237
+ }
238
+ const orgId = config.get('organizationId');
239
+ if (!orgId) {
240
+ console.log(chalk.red('✗ Organization ID not found. Please run "langctl auth <api-key>" again.\n'));
241
+ process.exitCode = 1;
242
+ return;
243
+ }
244
+ const spinner = ora(`Adding language ${language}...`).start();
245
+ try {
246
+ const project = await resolveProject(orgId, slug);
247
+ const api = getApiClient();
248
+ await api.post(`/orgs/${orgId}/projects/${project.id}/languages`, { code: language });
249
+ spinner.succeed(chalk.green(`Added language "${language}" to project "${slug}"`));
250
+ console.log('');
251
+ }
252
+ catch (error) {
253
+ spinner.fail(chalk.red('Failed to add language'));
254
+ console.error(chalk.red(`Error: ${error.message}\n`));
255
+ process.exitCode = 1;
256
+ }
257
+ }
258
+ /**
259
+ * Remove language from project
260
+ */
261
+ export async function projectsRemoveLanguageCommand(slug, language) {
262
+ if (!isAuthenticated()) {
263
+ console.log(chalk.red('✗ Not authenticated. Please run "langctl auth <api-key>" first.\n'));
264
+ process.exitCode = 1;
265
+ return;
266
+ }
267
+ const orgId = config.get('organizationId');
268
+ if (!orgId) {
269
+ console.log(chalk.red('✗ Organization ID not found. Please run "langctl auth <api-key>" again.\n'));
270
+ process.exitCode = 1;
271
+ return;
272
+ }
273
+ const spinner = ora(`Removing language ${language}...`).start();
274
+ try {
275
+ const project = await resolveProject(orgId, slug);
276
+ const api = getApiClient();
277
+ await api.delete(`/orgs/${orgId}/projects/${project.id}/languages/${language}`);
278
+ spinner.succeed(chalk.green(`Removed language "${language}" from project "${slug}"`));
279
+ console.log('');
280
+ }
281
+ catch (error) {
282
+ spinner.fail(chalk.red('Failed to remove language'));
283
+ console.error(chalk.red(`Error: ${error.message}\n`));
284
+ process.exitCode = 1;
285
+ }
286
+ }
287
+ /**
288
+ * Get project statistics
289
+ */
290
+ export async function projectsStatsCommand(slug) {
291
+ if (!isAuthenticated()) {
292
+ console.log(chalk.red('✗ Not authenticated. Please run "langctl auth <api-key>" first.\n'));
293
+ process.exitCode = 1;
294
+ return;
295
+ }
296
+ const orgId = config.get('organizationId');
297
+ if (!orgId) {
298
+ console.log(chalk.red('✗ Organization ID not found. Please run "langctl auth <api-key>" again.\n'));
299
+ process.exitCode = 1;
300
+ return;
301
+ }
302
+ const spinner = ora('Fetching statistics...').start();
303
+ try {
304
+ const project = await resolveProject(orgId, slug);
305
+ const api = getApiClient();
306
+ const stats = await api.get(`/orgs/${orgId}/projects/${project.id}/stats`);
307
+ spinner.stop();
308
+ console.log(chalk.blue.bold('\n📊 Project Statistics\n'));
309
+ console.log(chalk.white(`Total Keys: ${stats.totalKeys}`));
310
+ console.log(chalk.green(`Published Keys: ${stats.publishedKeys}`));
311
+ console.log(chalk.yellow(`Unpublished Keys: ${stats.unpublishedKeys}`));
312
+ console.log(chalk.white(`Language Count: ${stats.languageCount}`));
313
+ if (stats.modules && stats.modules.length > 0) {
314
+ console.log(chalk.white(`Modules: ${stats.modules.length}`));
315
+ console.log(chalk.gray(` ${stats.modules.join(', ')}`));
316
+ }
317
+ console.log('');
318
+ }
319
+ catch (error) {
320
+ spinner.fail(chalk.red('Failed to fetch statistics'));
321
+ console.error(chalk.red(`Error: ${error.message}\n`));
322
+ process.exitCode = 1;
52
323
  }
53
324
  }
54
325
  //# sourceMappingURL=projects.js.map
@@ -2,8 +2,9 @@ import chalk from 'chalk';
2
2
  import ora from 'ora';
3
3
  import { writeFileSync, mkdirSync } from 'fs';
4
4
  import { dirname, join } from 'path';
5
- import { getSupabase } from '../supabase.js';
6
- import { isAuthenticated, getOrganizationId } from '../auth.js';
5
+ import { isAuthenticated } from '../auth.js';
6
+ import { getApiClient } from '../api.js';
7
+ import { config } from '../config.js';
7
8
  import { exportTranslations } from '../exporters/index.js';
8
9
  /**
9
10
  * Get platform-specific output directory
@@ -18,64 +19,24 @@ function getPlatformDirectory(format) {
18
19
  };
19
20
  return platformDirs[format] || 'i18n';
20
21
  }
21
- /**
22
- * Resolve project identifier (name, slug, or UUID) to project UUID
23
- */
24
- async function resolveProjectId(identifier, organizationId) {
25
- const supabase = getSupabase();
26
- // Check if it's already a UUID (format: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)
27
- const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
28
- if (uuidRegex.test(identifier)) {
29
- // Verify it exists
30
- const { data } = await supabase
31
- .from('projects')
32
- .select('id')
33
- .eq('id', identifier)
34
- .eq('organization_id', organizationId)
35
- .is('deleted_at', null)
36
- .single();
37
- return data ? identifier : null;
38
- }
39
- // Try to match by slug (case-insensitive)
40
- const { data: bySlug } = await supabase
41
- .from('projects')
42
- .select('id')
43
- .eq('organization_id', organizationId)
44
- .ilike('slug', identifier)
45
- .is('deleted_at', null)
46
- .single();
47
- if (bySlug) {
48
- return bySlug.id;
49
- }
50
- // Try to match by name (case-insensitive)
51
- const { data: byName } = await supabase
52
- .from('projects')
53
- .select('id')
54
- .eq('organization_id', organizationId)
55
- .ilike('name', identifier)
56
- .is('deleted_at', null)
57
- .single();
58
- if (byName) {
59
- return byName.id;
60
- }
61
- return null;
62
- }
63
22
  export async function pullCommand(projectIdentifier, options) {
64
23
  // Check authentication
65
24
  if (!isAuthenticated()) {
66
25
  console.log(chalk.red('✗ Not authenticated. Please run "langctl auth <api-key>" first.\n'));
26
+ process.exitCode = 1;
67
27
  return;
68
28
  }
69
- const organizationId = getOrganizationId();
70
- if (!organizationId) {
71
- console.log(chalk.red('✗ Organization ID not found. Please run "langctl init" again.\n'));
29
+ const orgId = config.get('organizationId');
30
+ if (!orgId) {
31
+ console.log(chalk.red('✗ Organization ID not found. Please run "langctl auth <api-key>" again.\n'));
32
+ process.exitCode = 1;
72
33
  return;
73
34
  }
74
35
  // Set defaults
75
36
  const format = (options.format || 'json');
76
37
  const publishedOnly = options.publishedOnly !== false; // Default to true
77
- const specificLanguage = options.language; // If not specified, pull all languages
78
- const baseDir = options.dir || './translations'; // Default to ./translations
38
+ const specificLanguage = options.language;
39
+ const baseDir = options.dir || './translations';
79
40
  // Validate format
80
41
  const validFormats = ['json', 'json-nested', 'ios', 'android', 'flutter'];
81
42
  if (!validFormats.includes(format)) {
@@ -85,31 +46,17 @@ export async function pullCommand(projectIdentifier, options) {
85
46
  }
86
47
  const spinner = ora('Resolving project...').start();
87
48
  try {
88
- const supabase = getSupabase();
89
- // Resolve project identifier to UUID
90
- const projectId = await resolveProjectId(projectIdentifier, organizationId);
91
- if (!projectId) {
49
+ const api = getApiClient();
50
+ // Resolve project by slug
51
+ const project = await api.get(`/orgs/${orgId}/projects/by-slug/${projectIdentifier}`);
52
+ if (!project) {
92
53
  spinner.fail(chalk.red(`Project "${projectIdentifier}" not found`));
93
54
  console.log(chalk.yellow('\nRun "langctl projects list" to see available projects.\n'));
94
55
  return;
95
56
  }
96
- // Fetch project details
97
- spinner.text = 'Fetching project...';
98
- const { data: project, error: projectError } = await supabase
99
- .from('projects')
100
- .select('id, name, slug, languages, default_language')
101
- .eq('id', projectId)
102
- .eq('organization_id', organizationId)
103
- .is('deleted_at', null)
104
- .single();
105
- if (projectError || !project) {
106
- spinner.fail(chalk.red('Project not found or access denied'));
107
- return;
108
- }
109
57
  // Determine which languages to pull
110
58
  let languagesToPull;
111
59
  if (specificLanguage) {
112
- // Check if specified language exists in project
113
60
  if (!project.languages.includes(specificLanguage)) {
114
61
  spinner.fail(chalk.red(`Language "${specificLanguage}" not found in project`));
115
62
  console.log(chalk.yellow(`Available languages: ${project.languages.join(', ')}\n`));
@@ -118,44 +65,37 @@ export async function pullCommand(projectIdentifier, options) {
118
65
  languagesToPull = [specificLanguage];
119
66
  }
120
67
  else {
121
- // Pull all languages
122
68
  languagesToPull = project.languages;
123
69
  }
124
70
  spinner.text = `Fetching translations for "${project.name}"...`;
125
- // Fetch translation keys
126
- let query = supabase
127
- .from('translation_keys')
128
- .select('key, translations, description, module')
129
- .eq('project_id', projectId)
130
- .is('deleted_at', null);
131
- if (publishedOnly) {
132
- query = query.eq('published', true);
133
- }
134
- const { data: keys, error: keysError } = await query;
135
- if (keysError) {
136
- spinner.fail(chalk.red('Failed to fetch translations'));
137
- console.error(chalk.red(`Error: ${keysError.message}\n`));
138
- return;
139
- }
140
- if (!keys || keys.length === 0) {
141
- spinner.fail(chalk.yellow('No translations found'));
142
- console.log(chalk.yellow(`\nNo ${publishedOnly ? 'published ' : ''}translations found for this project.\n`));
143
- return;
144
- }
145
- spinner.succeed(chalk.green(`Found ${keys.length} translation keys`));
146
71
  // Get platform directory
147
72
  const platformDir = getPlatformDirectory(format);
148
73
  // Export translations for each language
149
74
  const exportedFiles = [];
75
+ let totalKeyCount = 0;
150
76
  for (const language of languagesToPull) {
151
- spinner.start(`Exporting ${language}...`);
152
- const result = exportTranslations(keys, language, format);
77
+ spinner.text = `Exporting ${language}...`;
78
+ // Fetch flat translations from the API
79
+ const result = await api.get(`/orgs/${orgId}/projects/${project.id}/export`, {
80
+ language,
81
+ publishedOnly: publishedOnly ? 'true' : 'false'
82
+ });
83
+ // Convert flat translations to TranslationKey array for the exporter
84
+ const flatTranslations = result.translations || {};
85
+ const translationKeys = Object.entries(flatTranslations).map(([key, value]) => ({
86
+ key,
87
+ translations: { [language]: value }
88
+ }));
89
+ if (totalKeyCount === 0) {
90
+ totalKeyCount = translationKeys.length;
91
+ }
92
+ const exportResult = exportTranslations(translationKeys, language, format);
153
93
  // Determine output path
154
94
  let outputPath;
155
95
  if (options.output) {
156
96
  // If custom output is specified and multiple languages, append language code
157
97
  if (languagesToPull.length > 1) {
158
- const ext = result.filename.split('.').pop();
98
+ const ext = exportResult.filename.split('.').pop();
159
99
  const base = options.output.replace(/\.[^.]+$/, '');
160
100
  outputPath = `${base}-${language}.${ext}`;
161
101
  }
@@ -165,15 +105,19 @@ export async function pullCommand(projectIdentifier, options) {
165
105
  }
166
106
  else {
167
107
  // Default output path: <baseDir>/<platform>/<language>.<ext>
168
- const ext = result.filename.split('.').pop();
108
+ const ext = exportResult.filename.split('.').pop();
169
109
  outputPath = join(baseDir, platformDir, `${language}.${ext}`);
170
110
  }
171
111
  // Create directory if it doesn't exist
172
112
  const dir = dirname(outputPath);
173
113
  mkdirSync(dir, { recursive: true });
174
- writeFileSync(outputPath, result.content, 'utf-8');
114
+ writeFileSync(outputPath, exportResult.content, 'utf-8');
175
115
  exportedFiles.push(outputPath);
176
116
  spinner.succeed(chalk.green(`Exported ${language} → ${outputPath}`));
117
+ // Restart spinner if more languages remain
118
+ if (languagesToPull.indexOf(language) < languagesToPull.length - 1) {
119
+ spinner.start();
120
+ }
177
121
  }
178
122
  // Summary
179
123
  console.log(chalk.hex('#10b981').bold('\n📦 Export Summary:'));
@@ -181,7 +125,7 @@ export async function pullCommand(projectIdentifier, options) {
181
125
  console.log(chalk.white(` Format: ${format}`));
182
126
  console.log(chalk.white(` Platform: ${platformDir}`));
183
127
  console.log(chalk.white(` Output: ${baseDir}`));
184
- console.log(chalk.white(` Keys: ${keys.length}`));
128
+ console.log(chalk.white(` Keys: ${totalKeyCount}`));
185
129
  console.log(chalk.white(` Languages: ${languagesToPull.join(', ')}`));
186
130
  console.log(chalk.white(` Files: ${exportedFiles.length}`));
187
131
  console.log('');
@@ -189,6 +133,7 @@ export async function pullCommand(projectIdentifier, options) {
189
133
  catch (error) {
190
134
  spinner.fail(chalk.red('Failed to pull translations'));
191
135
  console.error(chalk.red(`Error: ${error.message}\n`));
136
+ process.exitCode = 1;
192
137
  }
193
138
  }
194
139
  //# sourceMappingURL=pull.js.map
@@ -0,0 +1,29 @@
1
+ /**
2
+ * List all team members
3
+ */
4
+ export declare function listTeamCommand(): Promise<void>;
5
+ /**
6
+ * Get team member details
7
+ */
8
+ export declare function getTeamMemberCommand(email: string): Promise<void>;
9
+ /**
10
+ * Invite team member
11
+ */
12
+ export declare function inviteTeamMemberCommand(email: string, options: any): Promise<void>;
13
+ /**
14
+ * Remove team member
15
+ */
16
+ export declare function removeTeamMemberCommand(email: string): Promise<void>;
17
+ /**
18
+ * Update team member role
19
+ */
20
+ export declare function updateTeamRoleCommand(email: string, role: string): Promise<void>;
21
+ /**
22
+ * List invitations
23
+ */
24
+ export declare function listInvitationsCommand(options: any): Promise<void>;
25
+ /**
26
+ * Revoke invitation
27
+ */
28
+ export declare function revokeInvitationCommand(email: string): Promise<void>;
29
+ //# sourceMappingURL=team.d.ts.map