langctl 0.1.2 → 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.
- package/.github/ISSUE_TEMPLATE/bug_report.md +26 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +19 -0
- package/CONTRIBUTING.md +33 -0
- package/LICENSE +1 -1
- package/README.md +52 -11
- package/dist/api.d.ts +20 -0
- package/dist/api.js +75 -0
- package/dist/auth.d.ts +3 -2
- package/dist/auth.js +21 -27
- package/dist/commands/auth.js +2 -10
- package/dist/commands/config.js +1 -0
- package/dist/commands/debug.js +45 -41
- package/dist/commands/export.d.ts +1 -2
- package/dist/commands/export.js +70 -60
- package/dist/commands/import.d.ts +1 -0
- package/dist/commands/import.js +51 -72
- package/dist/commands/init.js +0 -10
- package/dist/commands/keys.js +122 -224
- package/dist/commands/org.js +43 -72
- package/dist/commands/projects.d.ts +3 -0
- package/dist/commands/projects.js +146 -223
- package/dist/commands/pull.js +40 -95
- package/dist/commands/team.js +115 -130
- package/dist/config.d.ts +1 -0
- package/dist/index.js +3 -3
- package/package.json +2 -3
- package/NEXT_PHASE.md +0 -618
- package/WORKING_INSTRUCTIONS.md +0 -1389
- package/dist/supabase.d.ts +0 -14
- package/dist/supabase.js +0 -30
|
@@ -1,62 +1,60 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import ora from 'ora';
|
|
3
|
-
import { isAuthenticated
|
|
4
|
-
|
|
5
|
-
|
|
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
|
+
*/
|
|
6
16
|
export async function projectsListCommand() {
|
|
7
|
-
// Check authentication
|
|
8
17
|
if (!isAuthenticated()) {
|
|
9
18
|
console.log(chalk.red('✗ Not authenticated. Please run "langctl auth <api-key>" first.\n'));
|
|
19
|
+
process.exitCode = 1;
|
|
10
20
|
return;
|
|
11
21
|
}
|
|
12
|
-
const
|
|
13
|
-
if (!
|
|
14
|
-
console.log(chalk.red('✗
|
|
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;
|
|
15
26
|
return;
|
|
16
27
|
}
|
|
17
28
|
const spinner = ora('Fetching projects...').start();
|
|
18
29
|
try {
|
|
19
|
-
|
|
20
|
-
const
|
|
21
|
-
method: 'POST',
|
|
22
|
-
headers: {
|
|
23
|
-
'Content-Type': 'application/json',
|
|
24
|
-
'X-API-Key': apiKey
|
|
25
|
-
}
|
|
26
|
-
});
|
|
27
|
-
if (!response.ok) {
|
|
28
|
-
const errorData = await response.json().catch(() => ({}));
|
|
29
|
-
throw new Error(errorData.error || `HTTP ${response.status}: ${response.statusText}`);
|
|
30
|
-
}
|
|
31
|
-
const data = await response.json();
|
|
32
|
-
if (!data.success) {
|
|
33
|
-
throw new Error(data.error || 'Failed to fetch projects');
|
|
34
|
-
}
|
|
30
|
+
const api = getApiClient();
|
|
31
|
+
const projects = await api.get(`/orgs/${orgId}/projects`);
|
|
35
32
|
spinner.stop();
|
|
36
|
-
if (!
|
|
33
|
+
if (!projects || projects.length === 0) {
|
|
37
34
|
console.log(chalk.yellow('\nNo projects found. Create one in the dashboard first.\n'));
|
|
38
35
|
return;
|
|
39
36
|
}
|
|
40
|
-
console.log(chalk.blue.bold(`\n📦 Projects (${
|
|
41
|
-
|
|
37
|
+
console.log(chalk.blue.bold(`\n📦 Projects (${projects.length})\n`));
|
|
38
|
+
projects.forEach((project) => {
|
|
42
39
|
console.log(chalk.white.bold(project.name));
|
|
43
40
|
console.log(chalk.gray(` Slug: ${project.slug}`));
|
|
44
41
|
if (project.description) {
|
|
45
42
|
console.log(chalk.gray(` Description: ${project.description}`));
|
|
46
43
|
}
|
|
47
44
|
console.log(chalk.gray(` Languages: ${project.languages.join(', ')}`));
|
|
48
|
-
console.log(chalk.gray(` Default: ${project.
|
|
45
|
+
console.log(chalk.gray(` Default: ${project.defaultLanguage}`));
|
|
49
46
|
if (project.modules && project.modules.length > 0) {
|
|
50
47
|
console.log(chalk.gray(` Modules: ${project.modules.join(', ')}`));
|
|
51
48
|
}
|
|
52
49
|
console.log('');
|
|
53
50
|
});
|
|
54
51
|
console.log(chalk.blue('To export translations, run:'));
|
|
55
|
-
console.log(chalk.white(` langctl export ${
|
|
52
|
+
console.log(chalk.white(` langctl export ${projects[0].slug} -l en\n`));
|
|
56
53
|
}
|
|
57
54
|
catch (error) {
|
|
58
55
|
spinner.fail(chalk.red('Failed to fetch projects'));
|
|
59
56
|
console.error(chalk.red(`Error: ${error.message}\n`));
|
|
57
|
+
process.exitCode = 1;
|
|
60
58
|
}
|
|
61
59
|
}
|
|
62
60
|
/**
|
|
@@ -65,37 +63,33 @@ export async function projectsListCommand() {
|
|
|
65
63
|
export async function projectsCreateCommand(name, options) {
|
|
66
64
|
if (!isAuthenticated()) {
|
|
67
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;
|
|
68
73
|
return;
|
|
69
74
|
}
|
|
70
|
-
const apiKey = getApiKey();
|
|
71
75
|
const spinner = ora('Creating project...').start();
|
|
72
76
|
try {
|
|
77
|
+
const api = getApiClient();
|
|
73
78
|
const languages = options.languages ? options.languages.split(',') : ['en'];
|
|
74
79
|
const defaultLanguage = options.defaultLanguage || languages[0];
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
},
|
|
81
|
-
body: JSON.stringify({
|
|
82
|
-
action: 'create',
|
|
83
|
-
name,
|
|
84
|
-
description: options.description,
|
|
85
|
-
languages,
|
|
86
|
-
defaultLanguage
|
|
87
|
-
})
|
|
80
|
+
const project = await api.post(`/orgs/${orgId}/projects`, {
|
|
81
|
+
name,
|
|
82
|
+
description: options.description,
|
|
83
|
+
languages,
|
|
84
|
+
defaultLanguage
|
|
88
85
|
});
|
|
89
|
-
|
|
90
|
-
if (!data.success) {
|
|
91
|
-
throw new Error(data.error);
|
|
92
|
-
}
|
|
93
|
-
spinner.succeed(chalk.green(`Created project: ${name} (${data.project.slug})`));
|
|
86
|
+
spinner.succeed(chalk.green(`Created project: ${name} (${project.slug})`));
|
|
94
87
|
console.log('');
|
|
95
88
|
}
|
|
96
89
|
catch (error) {
|
|
97
90
|
spinner.fail(chalk.red('Failed to create project'));
|
|
98
91
|
console.error(chalk.red(`Error: ${error.message}\n`));
|
|
92
|
+
process.exitCode = 1;
|
|
99
93
|
}
|
|
100
94
|
}
|
|
101
95
|
/**
|
|
@@ -104,25 +98,18 @@ export async function projectsCreateCommand(name, options) {
|
|
|
104
98
|
export async function projectsGetCommand(slug) {
|
|
105
99
|
if (!isAuthenticated()) {
|
|
106
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;
|
|
107
108
|
return;
|
|
108
109
|
}
|
|
109
|
-
const apiKey = getApiKey();
|
|
110
110
|
const spinner = ora('Fetching project...').start();
|
|
111
111
|
try {
|
|
112
|
-
|
|
113
|
-
const listResponse = await fetch(LIST_PROJECTS_URL, {
|
|
114
|
-
method: 'POST',
|
|
115
|
-
headers: {
|
|
116
|
-
'Content-Type': 'application/json',
|
|
117
|
-
'X-API-Key': apiKey
|
|
118
|
-
}
|
|
119
|
-
});
|
|
120
|
-
const listData = await listResponse.json();
|
|
121
|
-
const project = listData.projects?.find((p) => p.slug === slug);
|
|
122
|
-
if (!project) {
|
|
123
|
-
spinner.fail(chalk.red(`Project "${slug}" not found`));
|
|
124
|
-
return;
|
|
125
|
-
}
|
|
112
|
+
const project = await resolveProject(orgId, slug);
|
|
126
113
|
spinner.stop();
|
|
127
114
|
console.log(chalk.blue.bold('\n📦 Project Details\n'));
|
|
128
115
|
console.log(chalk.white.bold(project.name));
|
|
@@ -132,15 +119,16 @@ export async function projectsGetCommand(slug) {
|
|
|
132
119
|
console.log(chalk.gray(`Description: ${project.description}`));
|
|
133
120
|
}
|
|
134
121
|
console.log(chalk.gray(`Languages: ${project.languages.join(', ')}`));
|
|
135
|
-
console.log(chalk.gray(`Default Language: ${project.
|
|
122
|
+
console.log(chalk.gray(`Default Language: ${project.defaultLanguage}`));
|
|
136
123
|
if (project.modules && project.modules.length > 0) {
|
|
137
124
|
console.log(chalk.gray(`Modules: ${project.modules.join(', ')}`));
|
|
138
125
|
}
|
|
139
126
|
console.log('');
|
|
140
127
|
}
|
|
141
128
|
catch (error) {
|
|
142
|
-
spinner.fail(chalk.red(
|
|
129
|
+
spinner.fail(chalk.red(`Failed to fetch project "${slug}"`));
|
|
143
130
|
console.error(chalk.red(`Error: ${error.message}\n`));
|
|
131
|
+
process.exitCode = 1;
|
|
144
132
|
}
|
|
145
133
|
}
|
|
146
134
|
/**
|
|
@@ -149,45 +137,56 @@ export async function projectsGetCommand(slug) {
|
|
|
149
137
|
export async function projectsUpdateCommand(slug, options) {
|
|
150
138
|
if (!isAuthenticated()) {
|
|
151
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;
|
|
152
147
|
return;
|
|
153
148
|
}
|
|
154
|
-
const apiKey = getApiKey();
|
|
155
149
|
const spinner = ora('Updating project...').start();
|
|
156
150
|
try {
|
|
157
|
-
|
|
158
|
-
const
|
|
159
|
-
|
|
160
|
-
headers: {
|
|
161
|
-
'Content-Type': 'application/json',
|
|
162
|
-
'X-API-Key': apiKey
|
|
163
|
-
}
|
|
164
|
-
});
|
|
165
|
-
const listData = await listResponse.json();
|
|
166
|
-
const project = listData.projects?.find((p) => p.slug === slug);
|
|
167
|
-
if (!project) {
|
|
168
|
-
spinner.fail(chalk.red(`Project "${slug}" not found`));
|
|
169
|
-
return;
|
|
170
|
-
}
|
|
171
|
-
const updateData = { projectId: project.id, action: 'update' };
|
|
151
|
+
const project = await resolveProject(orgId, slug);
|
|
152
|
+
const api = getApiClient();
|
|
153
|
+
const updateData = {};
|
|
172
154
|
if (options.name)
|
|
173
155
|
updateData.name = options.name;
|
|
174
156
|
if (options.description !== undefined)
|
|
175
157
|
updateData.description = options.description;
|
|
176
|
-
if (options.languages)
|
|
177
|
-
updateData.languages = options.languages.split(',');
|
|
178
158
|
if (options.defaultLanguage)
|
|
179
159
|
updateData.defaultLanguage = options.defaultLanguage;
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
'
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
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
|
+
}
|
|
191
190
|
}
|
|
192
191
|
spinner.succeed(chalk.green(`Updated project: ${slug}`));
|
|
193
192
|
console.log('');
|
|
@@ -195,6 +194,7 @@ export async function projectsUpdateCommand(slug, options) {
|
|
|
195
194
|
catch (error) {
|
|
196
195
|
spinner.fail(chalk.red('Failed to update project'));
|
|
197
196
|
console.error(chalk.red(`Error: ${error.message}\n`));
|
|
197
|
+
process.exitCode = 1;
|
|
198
198
|
}
|
|
199
199
|
}
|
|
200
200
|
/**
|
|
@@ -203,46 +203,27 @@ export async function projectsUpdateCommand(slug, options) {
|
|
|
203
203
|
export async function projectsDeleteCommand(slug) {
|
|
204
204
|
if (!isAuthenticated()) {
|
|
205
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;
|
|
206
213
|
return;
|
|
207
214
|
}
|
|
208
|
-
const apiKey = getApiKey();
|
|
209
215
|
const spinner = ora('Deleting project...').start();
|
|
210
216
|
try {
|
|
211
|
-
|
|
212
|
-
const
|
|
213
|
-
|
|
214
|
-
headers: {
|
|
215
|
-
'Content-Type': 'application/json',
|
|
216
|
-
'X-API-Key': apiKey
|
|
217
|
-
}
|
|
218
|
-
});
|
|
219
|
-
const listData = await listResponse.json();
|
|
220
|
-
const project = listData.projects?.find((p) => p.slug === slug);
|
|
221
|
-
if (!project) {
|
|
222
|
-
spinner.fail(chalk.red(`Project "${slug}" not found`));
|
|
223
|
-
return;
|
|
224
|
-
}
|
|
225
|
-
const response = await fetch(MANAGE_PROJECTS_URL, {
|
|
226
|
-
method: 'POST',
|
|
227
|
-
headers: {
|
|
228
|
-
'Content-Type': 'application/json',
|
|
229
|
-
'X-API-Key': apiKey
|
|
230
|
-
},
|
|
231
|
-
body: JSON.stringify({
|
|
232
|
-
action: 'delete',
|
|
233
|
-
projectId: project.id
|
|
234
|
-
})
|
|
235
|
-
});
|
|
236
|
-
const data = await response.json();
|
|
237
|
-
if (!data.success) {
|
|
238
|
-
throw new Error(data.error);
|
|
239
|
-
}
|
|
217
|
+
const project = await resolveProject(orgId, slug);
|
|
218
|
+
const api = getApiClient();
|
|
219
|
+
await api.delete(`/orgs/${orgId}/projects/${project.id}`);
|
|
240
220
|
spinner.succeed(chalk.green(`Deleted project: ${slug}`));
|
|
241
221
|
console.log('');
|
|
242
222
|
}
|
|
243
223
|
catch (error) {
|
|
244
224
|
spinner.fail(chalk.red('Failed to delete project'));
|
|
245
225
|
console.error(chalk.red(`Error: ${error.message}\n`));
|
|
226
|
+
process.exitCode = 1;
|
|
246
227
|
}
|
|
247
228
|
}
|
|
248
229
|
/**
|
|
@@ -251,47 +232,27 @@ export async function projectsDeleteCommand(slug) {
|
|
|
251
232
|
export async function projectsAddLanguageCommand(slug, language) {
|
|
252
233
|
if (!isAuthenticated()) {
|
|
253
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;
|
|
254
242
|
return;
|
|
255
243
|
}
|
|
256
|
-
const apiKey = getApiKey();
|
|
257
244
|
const spinner = ora(`Adding language ${language}...`).start();
|
|
258
245
|
try {
|
|
259
|
-
|
|
260
|
-
const
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
'Content-Type': 'application/json',
|
|
264
|
-
'X-API-Key': apiKey
|
|
265
|
-
}
|
|
266
|
-
});
|
|
267
|
-
const listData = await listResponse.json();
|
|
268
|
-
const project = listData.projects?.find((p) => p.slug === slug);
|
|
269
|
-
if (!project) {
|
|
270
|
-
spinner.fail(chalk.red(`Project "${slug}" not found`));
|
|
271
|
-
return;
|
|
272
|
-
}
|
|
273
|
-
const response = await fetch(MANAGE_PROJECTS_URL, {
|
|
274
|
-
method: 'POST',
|
|
275
|
-
headers: {
|
|
276
|
-
'Content-Type': 'application/json',
|
|
277
|
-
'X-API-Key': apiKey
|
|
278
|
-
},
|
|
279
|
-
body: JSON.stringify({
|
|
280
|
-
action: 'add-language',
|
|
281
|
-
projectId: project.id,
|
|
282
|
-
language
|
|
283
|
-
})
|
|
284
|
-
});
|
|
285
|
-
const data = await response.json();
|
|
286
|
-
if (!data.success) {
|
|
287
|
-
throw new Error(data.error);
|
|
288
|
-
}
|
|
289
|
-
spinner.succeed(chalk.green(data.message));
|
|
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}"`));
|
|
290
250
|
console.log('');
|
|
291
251
|
}
|
|
292
252
|
catch (error) {
|
|
293
253
|
spinner.fail(chalk.red('Failed to add language'));
|
|
294
254
|
console.error(chalk.red(`Error: ${error.message}\n`));
|
|
255
|
+
process.exitCode = 1;
|
|
295
256
|
}
|
|
296
257
|
}
|
|
297
258
|
/**
|
|
@@ -300,47 +261,27 @@ export async function projectsAddLanguageCommand(slug, language) {
|
|
|
300
261
|
export async function projectsRemoveLanguageCommand(slug, language) {
|
|
301
262
|
if (!isAuthenticated()) {
|
|
302
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;
|
|
303
271
|
return;
|
|
304
272
|
}
|
|
305
|
-
const apiKey = getApiKey();
|
|
306
273
|
const spinner = ora(`Removing language ${language}...`).start();
|
|
307
274
|
try {
|
|
308
|
-
|
|
309
|
-
const
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
'Content-Type': 'application/json',
|
|
313
|
-
'X-API-Key': apiKey
|
|
314
|
-
}
|
|
315
|
-
});
|
|
316
|
-
const listData = await listResponse.json();
|
|
317
|
-
const project = listData.projects?.find((p) => p.slug === slug);
|
|
318
|
-
if (!project) {
|
|
319
|
-
spinner.fail(chalk.red(`Project "${slug}" not found`));
|
|
320
|
-
return;
|
|
321
|
-
}
|
|
322
|
-
const response = await fetch(MANAGE_PROJECTS_URL, {
|
|
323
|
-
method: 'POST',
|
|
324
|
-
headers: {
|
|
325
|
-
'Content-Type': 'application/json',
|
|
326
|
-
'X-API-Key': apiKey
|
|
327
|
-
},
|
|
328
|
-
body: JSON.stringify({
|
|
329
|
-
action: 'remove-language',
|
|
330
|
-
projectId: project.id,
|
|
331
|
-
language
|
|
332
|
-
})
|
|
333
|
-
});
|
|
334
|
-
const data = await response.json();
|
|
335
|
-
if (!data.success) {
|
|
336
|
-
throw new Error(data.error);
|
|
337
|
-
}
|
|
338
|
-
spinner.succeed(chalk.green(data.message));
|
|
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}"`));
|
|
339
279
|
console.log('');
|
|
340
280
|
}
|
|
341
281
|
catch (error) {
|
|
342
282
|
spinner.fail(chalk.red('Failed to remove language'));
|
|
343
283
|
console.error(chalk.red(`Error: ${error.message}\n`));
|
|
284
|
+
process.exitCode = 1;
|
|
344
285
|
}
|
|
345
286
|
}
|
|
346
287
|
/**
|
|
@@ -349,54 +290,36 @@ export async function projectsRemoveLanguageCommand(slug, language) {
|
|
|
349
290
|
export async function projectsStatsCommand(slug) {
|
|
350
291
|
if (!isAuthenticated()) {
|
|
351
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;
|
|
352
300
|
return;
|
|
353
301
|
}
|
|
354
|
-
const apiKey = getApiKey();
|
|
355
302
|
const spinner = ora('Fetching statistics...').start();
|
|
356
303
|
try {
|
|
357
|
-
|
|
358
|
-
const
|
|
359
|
-
|
|
360
|
-
headers: {
|
|
361
|
-
'Content-Type': 'application/json',
|
|
362
|
-
'X-API-Key': apiKey
|
|
363
|
-
}
|
|
364
|
-
});
|
|
365
|
-
const listData = await listResponse.json();
|
|
366
|
-
const project = listData.projects?.find((p) => p.slug === slug);
|
|
367
|
-
if (!project) {
|
|
368
|
-
spinner.fail(chalk.red(`Project "${slug}" not found`));
|
|
369
|
-
return;
|
|
370
|
-
}
|
|
371
|
-
const response = await fetch(MANAGE_PROJECTS_URL, {
|
|
372
|
-
method: 'POST',
|
|
373
|
-
headers: {
|
|
374
|
-
'Content-Type': 'application/json',
|
|
375
|
-
'X-API-Key': apiKey
|
|
376
|
-
},
|
|
377
|
-
body: JSON.stringify({
|
|
378
|
-
action: 'stats',
|
|
379
|
-
projectId: project.id
|
|
380
|
-
})
|
|
381
|
-
});
|
|
382
|
-
const data = await response.json();
|
|
383
|
-
if (!data.success) {
|
|
384
|
-
throw new Error(data.error);
|
|
385
|
-
}
|
|
304
|
+
const project = await resolveProject(orgId, slug);
|
|
305
|
+
const api = getApiClient();
|
|
306
|
+
const stats = await api.get(`/orgs/${orgId}/projects/${project.id}/stats`);
|
|
386
307
|
spinner.stop();
|
|
387
308
|
console.log(chalk.blue.bold('\n📊 Project Statistics\n'));
|
|
388
|
-
console.log(chalk.white(`Total Keys: ${
|
|
389
|
-
console.log(chalk.green(`Published Keys: ${
|
|
390
|
-
console.log(chalk.yellow(`Unpublished Keys: ${
|
|
391
|
-
console.log(chalk.white(`
|
|
392
|
-
if (
|
|
393
|
-
console.log(chalk.
|
|
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(', ')}`));
|
|
394
316
|
}
|
|
395
317
|
console.log('');
|
|
396
318
|
}
|
|
397
319
|
catch (error) {
|
|
398
320
|
spinner.fail(chalk.red('Failed to fetch statistics'));
|
|
399
321
|
console.error(chalk.red(`Error: ${error.message}\n`));
|
|
322
|
+
process.exitCode = 1;
|
|
400
323
|
}
|
|
401
324
|
}
|
|
402
325
|
//# sourceMappingURL=projects.js.map
|