langctl 0.1.1 ā 0.1.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/.gitattributes +2 -0
- package/NEXT_PHASE.md +618 -0
- package/README.md +971 -107
- package/WORKING_INSTRUCTIONS.md +1389 -0
- package/dist/auth.d.ts +4 -0
- package/dist/auth.js +6 -0
- package/dist/commands/export.d.ts +14 -0
- package/dist/commands/export.js +115 -0
- package/dist/commands/import.d.ts +11 -0
- package/dist/commands/import.js +171 -0
- package/dist/commands/keys.d.ts +25 -0
- package/dist/commands/keys.js +396 -0
- package/dist/commands/org.d.ts +13 -0
- package/dist/commands/org.js +147 -0
- package/dist/commands/projects.d.ts +28 -0
- package/dist/commands/projects.js +369 -21
- package/dist/commands/team.d.ts +29 -0
- package/dist/commands/team.js +285 -0
- package/dist/index.js +354 -3
- package/package.json +52 -5
- package/NPM_PUBLISH_GUIDE.md +0 -293
- package/QUICK_PUBLISH.md +0 -64
- package/SECURITY_FIX.md +0 -167
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import ora from 'ora';
|
|
3
|
+
import { isAuthenticated, getApiKey } from '../auth.js';
|
|
4
|
+
const TEAM_MANAGEMENT_URL = 'https://bcgnmvkgkbhbxzzflwdb.supabase.co/functions/v1/manage-team-members';
|
|
5
|
+
/**
|
|
6
|
+
* List all team members
|
|
7
|
+
*/
|
|
8
|
+
export async function listTeamCommand() {
|
|
9
|
+
if (!isAuthenticated()) {
|
|
10
|
+
console.log(chalk.red('ā Not authenticated. Please run "langctl auth <api-key>" first.\n'));
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
const apiKey = getApiKey();
|
|
14
|
+
const spinner = ora('Fetching team members...').start();
|
|
15
|
+
try {
|
|
16
|
+
const response = await fetch(TEAM_MANAGEMENT_URL, {
|
|
17
|
+
method: 'POST',
|
|
18
|
+
headers: {
|
|
19
|
+
'Content-Type': 'application/json',
|
|
20
|
+
'X-API-Key': apiKey
|
|
21
|
+
},
|
|
22
|
+
body: JSON.stringify({
|
|
23
|
+
action: 'list'
|
|
24
|
+
})
|
|
25
|
+
});
|
|
26
|
+
const data = await response.json();
|
|
27
|
+
if (!data.success) {
|
|
28
|
+
throw new Error(data.error);
|
|
29
|
+
}
|
|
30
|
+
spinner.stop();
|
|
31
|
+
if (!data.members || data.members.length === 0) {
|
|
32
|
+
console.log(chalk.yellow('\nNo team members found\n'));
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
console.log(chalk.blue.bold(`\nš„ Team Members (${data.members.length})\n`));
|
|
36
|
+
data.members.forEach((member) => {
|
|
37
|
+
console.log(chalk.white.bold(member.email || member.user_id));
|
|
38
|
+
console.log(chalk.gray(` Role: ${member.role}`));
|
|
39
|
+
if (member.joined_at) {
|
|
40
|
+
console.log(chalk.gray(` Joined: ${new Date(member.joined_at).toLocaleDateString()}`));
|
|
41
|
+
}
|
|
42
|
+
console.log('');
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
spinner.fail(chalk.red('Failed to list team members'));
|
|
47
|
+
console.error(chalk.red(`Error: ${error.message}\n`));
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Get team member details
|
|
52
|
+
*/
|
|
53
|
+
export async function getTeamMemberCommand(email) {
|
|
54
|
+
if (!isAuthenticated()) {
|
|
55
|
+
console.log(chalk.red('ā Not authenticated. Please run "langctl auth <api-key>" first.\n'));
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
const apiKey = getApiKey();
|
|
59
|
+
const spinner = ora('Fetching member details...').start();
|
|
60
|
+
try {
|
|
61
|
+
const response = await fetch(TEAM_MANAGEMENT_URL, {
|
|
62
|
+
method: 'POST',
|
|
63
|
+
headers: {
|
|
64
|
+
'Content-Type': 'application/json',
|
|
65
|
+
'X-API-Key': apiKey
|
|
66
|
+
},
|
|
67
|
+
body: JSON.stringify({
|
|
68
|
+
action: 'get',
|
|
69
|
+
email
|
|
70
|
+
})
|
|
71
|
+
});
|
|
72
|
+
const data = await response.json();
|
|
73
|
+
if (!data.success) {
|
|
74
|
+
throw new Error(data.error);
|
|
75
|
+
}
|
|
76
|
+
spinner.stop();
|
|
77
|
+
const member = data.member;
|
|
78
|
+
console.log(chalk.blue.bold('\nš¤ Team Member Details\n'));
|
|
79
|
+
console.log(chalk.white.bold(`Email: ${member.email}`));
|
|
80
|
+
console.log(chalk.gray(`Role: ${member.role}`));
|
|
81
|
+
console.log(chalk.gray(`User ID: ${member.user_id}`));
|
|
82
|
+
if (member.joined_at) {
|
|
83
|
+
console.log(chalk.gray(`Joined: ${new Date(member.joined_at).toLocaleString()}`));
|
|
84
|
+
}
|
|
85
|
+
console.log('');
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
spinner.fail(chalk.red('Failed to get member details'));
|
|
89
|
+
console.error(chalk.red(`Error: ${error.message}\n`));
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Invite team member
|
|
94
|
+
*/
|
|
95
|
+
export async function inviteTeamMemberCommand(email, options) {
|
|
96
|
+
if (!isAuthenticated()) {
|
|
97
|
+
console.log(chalk.red('ā Not authenticated. Please run "langctl auth <api-key>" first.\n'));
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
const apiKey = getApiKey();
|
|
101
|
+
const spinner = ora(`Inviting ${email}...`).start();
|
|
102
|
+
try {
|
|
103
|
+
const response = await fetch(TEAM_MANAGEMENT_URL, {
|
|
104
|
+
method: 'POST',
|
|
105
|
+
headers: {
|
|
106
|
+
'Content-Type': 'application/json',
|
|
107
|
+
'X-API-Key': apiKey
|
|
108
|
+
},
|
|
109
|
+
body: JSON.stringify({
|
|
110
|
+
action: 'invite',
|
|
111
|
+
email,
|
|
112
|
+
role: options.role || 'member'
|
|
113
|
+
})
|
|
114
|
+
});
|
|
115
|
+
const data = await response.json();
|
|
116
|
+
if (!data.success) {
|
|
117
|
+
throw new Error(data.error);
|
|
118
|
+
}
|
|
119
|
+
spinner.succeed(chalk.green(data.message));
|
|
120
|
+
console.log('');
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
spinner.fail(chalk.red('Failed to invite member'));
|
|
124
|
+
console.error(chalk.red(`Error: ${error.message}\n`));
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Remove team member
|
|
129
|
+
*/
|
|
130
|
+
export async function removeTeamMemberCommand(email) {
|
|
131
|
+
if (!isAuthenticated()) {
|
|
132
|
+
console.log(chalk.red('ā Not authenticated. Please run "langctl auth <api-key>" first.\n'));
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
const apiKey = getApiKey();
|
|
136
|
+
const spinner = ora(`Removing ${email}...`).start();
|
|
137
|
+
try {
|
|
138
|
+
const response = await fetch(TEAM_MANAGEMENT_URL, {
|
|
139
|
+
method: 'POST',
|
|
140
|
+
headers: {
|
|
141
|
+
'Content-Type': 'application/json',
|
|
142
|
+
'X-API-Key': apiKey
|
|
143
|
+
},
|
|
144
|
+
body: JSON.stringify({
|
|
145
|
+
action: 'remove',
|
|
146
|
+
email
|
|
147
|
+
})
|
|
148
|
+
});
|
|
149
|
+
const data = await response.json();
|
|
150
|
+
if (!data.success) {
|
|
151
|
+
throw new Error(data.error);
|
|
152
|
+
}
|
|
153
|
+
spinner.succeed(chalk.green(data.message));
|
|
154
|
+
console.log('');
|
|
155
|
+
}
|
|
156
|
+
catch (error) {
|
|
157
|
+
spinner.fail(chalk.red('Failed to remove member'));
|
|
158
|
+
console.error(chalk.red(`Error: ${error.message}\n`));
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Update team member role
|
|
163
|
+
*/
|
|
164
|
+
export async function updateTeamRoleCommand(email, role) {
|
|
165
|
+
if (!isAuthenticated()) {
|
|
166
|
+
console.log(chalk.red('ā Not authenticated. Please run "langctl auth <api-key>" first.\n'));
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
const validRoles = ['viewer', 'member', 'admin', 'owner'];
|
|
170
|
+
if (!validRoles.includes(role)) {
|
|
171
|
+
console.log(chalk.red(`ā Invalid role. Must be one of: ${validRoles.join(', ')}\n`));
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
const apiKey = getApiKey();
|
|
175
|
+
const spinner = ora(`Updating role for ${email}...`).start();
|
|
176
|
+
try {
|
|
177
|
+
const response = await fetch(TEAM_MANAGEMENT_URL, {
|
|
178
|
+
method: 'POST',
|
|
179
|
+
headers: {
|
|
180
|
+
'Content-Type': 'application/json',
|
|
181
|
+
'X-API-Key': apiKey
|
|
182
|
+
},
|
|
183
|
+
body: JSON.stringify({
|
|
184
|
+
action: 'update-role',
|
|
185
|
+
email,
|
|
186
|
+
role
|
|
187
|
+
})
|
|
188
|
+
});
|
|
189
|
+
const data = await response.json();
|
|
190
|
+
if (!data.success) {
|
|
191
|
+
throw new Error(data.error);
|
|
192
|
+
}
|
|
193
|
+
spinner.succeed(chalk.green(data.message));
|
|
194
|
+
console.log('');
|
|
195
|
+
}
|
|
196
|
+
catch (error) {
|
|
197
|
+
spinner.fail(chalk.red('Failed to update role'));
|
|
198
|
+
console.error(chalk.red(`Error: ${error.message}\n`));
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* List invitations
|
|
203
|
+
*/
|
|
204
|
+
export async function listInvitationsCommand(options) {
|
|
205
|
+
if (!isAuthenticated()) {
|
|
206
|
+
console.log(chalk.red('ā Not authenticated. Please run "langctl auth <api-key>" first.\n'));
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
const apiKey = getApiKey();
|
|
210
|
+
const spinner = ora('Fetching invitations...').start();
|
|
211
|
+
try {
|
|
212
|
+
const response = await fetch(TEAM_MANAGEMENT_URL, {
|
|
213
|
+
method: 'POST',
|
|
214
|
+
headers: {
|
|
215
|
+
'Content-Type': 'application/json',
|
|
216
|
+
'X-API-Key': apiKey
|
|
217
|
+
},
|
|
218
|
+
body: JSON.stringify({
|
|
219
|
+
action: 'list-invitations',
|
|
220
|
+
pending: options.pending || false
|
|
221
|
+
})
|
|
222
|
+
});
|
|
223
|
+
const data = await response.json();
|
|
224
|
+
if (!data.success) {
|
|
225
|
+
throw new Error(data.error);
|
|
226
|
+
}
|
|
227
|
+
spinner.stop();
|
|
228
|
+
if (!data.invitations || data.invitations.length === 0) {
|
|
229
|
+
console.log(chalk.yellow('\nNo invitations found\n'));
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
console.log(chalk.blue.bold(`\nāļø Invitations (${data.invitations.length})\n`));
|
|
233
|
+
data.invitations.forEach((invitation) => {
|
|
234
|
+
console.log(chalk.white.bold(invitation.email));
|
|
235
|
+
console.log(chalk.gray(` Role: ${invitation.role}`));
|
|
236
|
+
console.log(chalk.gray(` Status: ${invitation.status}`));
|
|
237
|
+
if (invitation.invited_at) {
|
|
238
|
+
console.log(chalk.gray(` Invited: ${new Date(invitation.invited_at).toLocaleDateString()}`));
|
|
239
|
+
}
|
|
240
|
+
if (invitation.expires_at) {
|
|
241
|
+
console.log(chalk.gray(` Expires: ${new Date(invitation.expires_at).toLocaleDateString()}`));
|
|
242
|
+
}
|
|
243
|
+
console.log('');
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
catch (error) {
|
|
247
|
+
spinner.fail(chalk.red('Failed to list invitations'));
|
|
248
|
+
console.error(chalk.red(`Error: ${error.message}\n`));
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Revoke invitation
|
|
253
|
+
*/
|
|
254
|
+
export async function revokeInvitationCommand(email) {
|
|
255
|
+
if (!isAuthenticated()) {
|
|
256
|
+
console.log(chalk.red('ā Not authenticated. Please run "langctl auth <api-key>" first.\n'));
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
const apiKey = getApiKey();
|
|
260
|
+
const spinner = ora(`Revoking invitation for ${email}...`).start();
|
|
261
|
+
try {
|
|
262
|
+
const response = await fetch(TEAM_MANAGEMENT_URL, {
|
|
263
|
+
method: 'POST',
|
|
264
|
+
headers: {
|
|
265
|
+
'Content-Type': 'application/json',
|
|
266
|
+
'X-API-Key': apiKey
|
|
267
|
+
},
|
|
268
|
+
body: JSON.stringify({
|
|
269
|
+
action: 'revoke-invitation',
|
|
270
|
+
email
|
|
271
|
+
})
|
|
272
|
+
});
|
|
273
|
+
const data = await response.json();
|
|
274
|
+
if (!data.success) {
|
|
275
|
+
throw new Error(data.error);
|
|
276
|
+
}
|
|
277
|
+
spinner.succeed(chalk.green(data.message));
|
|
278
|
+
console.log('');
|
|
279
|
+
}
|
|
280
|
+
catch (error) {
|
|
281
|
+
spinner.fail(chalk.red('Failed to revoke invitation'));
|
|
282
|
+
console.error(chalk.red(`Error: ${error.message}\n`));
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
//# sourceMappingURL=team.js.map
|
package/dist/index.js
CHANGED
|
@@ -4,8 +4,13 @@ import chalk from 'chalk';
|
|
|
4
4
|
import { initCommand } from './commands/init.js';
|
|
5
5
|
import { authCommand, logoutCommand } from './commands/auth.js';
|
|
6
6
|
import { configCommand } from './commands/config.js';
|
|
7
|
-
import { projectsListCommand } from './commands/projects.js';
|
|
7
|
+
import { projectsListCommand, projectsCreateCommand, projectsGetCommand, projectsUpdateCommand, projectsDeleteCommand, projectsAddLanguageCommand, projectsRemoveLanguageCommand, projectsStatsCommand } from './commands/projects.js';
|
|
8
|
+
import { listKeysCommand, getKeyCommand, createKeyCommand, deleteKeyCommand, translateKeyCommand, publishKeysCommand } from './commands/keys.js';
|
|
9
|
+
import { listTeamCommand, getTeamMemberCommand, inviteTeamMemberCommand, removeTeamMemberCommand, updateTeamRoleCommand, listInvitationsCommand, revokeInvitationCommand } from './commands/team.js';
|
|
10
|
+
import { orgInfoCommand, orgStatsCommand, orgPlanCommand } from './commands/org.js';
|
|
8
11
|
import { pullCommand } from './commands/pull.js';
|
|
12
|
+
import { exportCommand } from './commands/export.js';
|
|
13
|
+
import { importCommand } from './commands/import.js';
|
|
9
14
|
import { debugListApiKeys, debugListOrganizations } from './commands/debug.js';
|
|
10
15
|
import { showLogo } from './utils/banner.js';
|
|
11
16
|
const program = new Command();
|
|
@@ -87,10 +92,322 @@ projects
|
|
|
87
92
|
process.exit(1);
|
|
88
93
|
}
|
|
89
94
|
});
|
|
90
|
-
|
|
95
|
+
projects
|
|
96
|
+
.command('create <name>')
|
|
97
|
+
.description('Create a new project')
|
|
98
|
+
.option('-d, --description <text>', 'Project description')
|
|
99
|
+
.option('-l, --languages <langs>', 'Comma-separated language codes (e.g., en,es,fr)', 'en')
|
|
100
|
+
.option('--default-language <code>', 'Default language code')
|
|
101
|
+
.action(async (name, options) => {
|
|
102
|
+
try {
|
|
103
|
+
await projectsCreateCommand(name, options);
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
console.error(chalk.red(`Error: ${error.message}\n`));
|
|
107
|
+
process.exit(1);
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
projects
|
|
111
|
+
.command('get <slug>')
|
|
112
|
+
.description('Get project details')
|
|
113
|
+
.action(async (slug) => {
|
|
114
|
+
try {
|
|
115
|
+
await projectsGetCommand(slug);
|
|
116
|
+
}
|
|
117
|
+
catch (error) {
|
|
118
|
+
console.error(chalk.red(`Error: ${error.message}\n`));
|
|
119
|
+
process.exit(1);
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
projects
|
|
123
|
+
.command('update <slug>')
|
|
124
|
+
.description('Update project')
|
|
125
|
+
.option('-n, --name <name>', 'New project name')
|
|
126
|
+
.option('-d, --description <text>', 'New description')
|
|
127
|
+
.option('-l, --languages <langs>', 'Comma-separated language codes')
|
|
128
|
+
.option('--default-language <code>', 'Default language code')
|
|
129
|
+
.action(async (slug, options) => {
|
|
130
|
+
try {
|
|
131
|
+
await projectsUpdateCommand(slug, options);
|
|
132
|
+
}
|
|
133
|
+
catch (error) {
|
|
134
|
+
console.error(chalk.red(`Error: ${error.message}\n`));
|
|
135
|
+
process.exit(1);
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
projects
|
|
139
|
+
.command('delete <slug>')
|
|
140
|
+
.description('Delete project')
|
|
141
|
+
.action(async (slug) => {
|
|
142
|
+
try {
|
|
143
|
+
await projectsDeleteCommand(slug);
|
|
144
|
+
}
|
|
145
|
+
catch (error) {
|
|
146
|
+
console.error(chalk.red(`Error: ${error.message}\n`));
|
|
147
|
+
process.exit(1);
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
projects
|
|
151
|
+
.command('add-language <slug> <language>')
|
|
152
|
+
.description('Add language to project')
|
|
153
|
+
.action(async (slug, language) => {
|
|
154
|
+
try {
|
|
155
|
+
await projectsAddLanguageCommand(slug, language);
|
|
156
|
+
}
|
|
157
|
+
catch (error) {
|
|
158
|
+
console.error(chalk.red(`Error: ${error.message}\n`));
|
|
159
|
+
process.exit(1);
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
projects
|
|
163
|
+
.command('remove-language <slug> <language>')
|
|
164
|
+
.description('Remove language from project')
|
|
165
|
+
.action(async (slug, language) => {
|
|
166
|
+
try {
|
|
167
|
+
await projectsRemoveLanguageCommand(slug, language);
|
|
168
|
+
}
|
|
169
|
+
catch (error) {
|
|
170
|
+
console.error(chalk.red(`Error: ${error.message}\n`));
|
|
171
|
+
process.exit(1);
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
projects
|
|
175
|
+
.command('stats <slug>')
|
|
176
|
+
.description('Get project statistics')
|
|
177
|
+
.action(async (slug) => {
|
|
178
|
+
try {
|
|
179
|
+
await projectsStatsCommand(slug);
|
|
180
|
+
}
|
|
181
|
+
catch (error) {
|
|
182
|
+
console.error(chalk.red(`Error: ${error.message}\n`));
|
|
183
|
+
process.exit(1);
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
// Keys command
|
|
187
|
+
const keys = program
|
|
188
|
+
.command('keys')
|
|
189
|
+
.description('Manage translation keys');
|
|
190
|
+
keys
|
|
191
|
+
.command('list <project>')
|
|
192
|
+
.description('List translation keys')
|
|
193
|
+
.option('-m, --module <name>', 'Filter by module')
|
|
194
|
+
.option('-p, --published', 'Show only published keys')
|
|
195
|
+
.option('-s, --search <term>', 'Search keys')
|
|
196
|
+
.option('--limit <number>', 'Limit results', '100')
|
|
197
|
+
.option('--offset <number>', 'Offset results', '0')
|
|
198
|
+
.action(async (project, options) => {
|
|
199
|
+
try {
|
|
200
|
+
await listKeysCommand(project, options);
|
|
201
|
+
}
|
|
202
|
+
catch (error) {
|
|
203
|
+
console.error(chalk.red(`Error: ${error.message}\n`));
|
|
204
|
+
process.exit(1);
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
keys
|
|
208
|
+
.command('get <project> <key>')
|
|
209
|
+
.description('Get key details')
|
|
210
|
+
.action(async (project, key) => {
|
|
211
|
+
try {
|
|
212
|
+
await getKeyCommand(project, key);
|
|
213
|
+
}
|
|
214
|
+
catch (error) {
|
|
215
|
+
console.error(chalk.red(`Error: ${error.message}\n`));
|
|
216
|
+
process.exit(1);
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
keys
|
|
220
|
+
.command('create <project> <key>')
|
|
221
|
+
.description('Create translation key')
|
|
222
|
+
.option('-d, --description <text>', 'Key description')
|
|
223
|
+
.option('-m, --module <name>', 'Module name')
|
|
224
|
+
.option('--value-en <value>', 'English value')
|
|
225
|
+
.option('--value-es <value>', 'Spanish value')
|
|
226
|
+
.option('--value-fr <value>', 'French value')
|
|
227
|
+
.option('--value-de <value>', 'German value')
|
|
228
|
+
.option('--tags <tags>', 'Comma-separated tags')
|
|
229
|
+
.action(async (project, key, options) => {
|
|
230
|
+
try {
|
|
231
|
+
await createKeyCommand(project, key, options);
|
|
232
|
+
}
|
|
233
|
+
catch (error) {
|
|
234
|
+
console.error(chalk.red(`Error: ${error.message}\n`));
|
|
235
|
+
process.exit(1);
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
keys
|
|
239
|
+
.command('delete <project> <key>')
|
|
240
|
+
.description('Delete translation key')
|
|
241
|
+
.action(async (project, key) => {
|
|
242
|
+
try {
|
|
243
|
+
await deleteKeyCommand(project, key);
|
|
244
|
+
}
|
|
245
|
+
catch (error) {
|
|
246
|
+
console.error(chalk.red(`Error: ${error.message}\n`));
|
|
247
|
+
process.exit(1);
|
|
248
|
+
}
|
|
249
|
+
});
|
|
250
|
+
keys
|
|
251
|
+
.command('translate <project> <key>')
|
|
252
|
+
.description('Update translation for a key')
|
|
253
|
+
.requiredOption('-l, --language <code>', 'Language code')
|
|
254
|
+
.requiredOption('-v, --value <text>', 'Translation value')
|
|
255
|
+
.action(async (project, key, options) => {
|
|
256
|
+
try {
|
|
257
|
+
await translateKeyCommand(project, key, options);
|
|
258
|
+
}
|
|
259
|
+
catch (error) {
|
|
260
|
+
console.error(chalk.red(`Error: ${error.message}\n`));
|
|
261
|
+
process.exit(1);
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
keys
|
|
265
|
+
.command('publish <project> <keys...>')
|
|
266
|
+
.description('Publish translation keys')
|
|
267
|
+
.option('--unpublish', 'Unpublish instead of publish')
|
|
268
|
+
.action(async (project, keysList, options) => {
|
|
269
|
+
try {
|
|
270
|
+
await publishKeysCommand(project, keysList, options);
|
|
271
|
+
}
|
|
272
|
+
catch (error) {
|
|
273
|
+
console.error(chalk.red(`Error: ${error.message}\n`));
|
|
274
|
+
process.exit(1);
|
|
275
|
+
}
|
|
276
|
+
});
|
|
277
|
+
// Team command
|
|
278
|
+
const team = program
|
|
279
|
+
.command('team')
|
|
280
|
+
.description('Manage team members');
|
|
281
|
+
team
|
|
282
|
+
.command('list')
|
|
283
|
+
.description('List team members')
|
|
284
|
+
.action(async () => {
|
|
285
|
+
try {
|
|
286
|
+
await listTeamCommand();
|
|
287
|
+
}
|
|
288
|
+
catch (error) {
|
|
289
|
+
console.error(chalk.red(`Error: ${error.message}\n`));
|
|
290
|
+
process.exit(1);
|
|
291
|
+
}
|
|
292
|
+
});
|
|
293
|
+
team
|
|
294
|
+
.command('get <email>')
|
|
295
|
+
.description('Get team member details')
|
|
296
|
+
.action(async (email) => {
|
|
297
|
+
try {
|
|
298
|
+
await getTeamMemberCommand(email);
|
|
299
|
+
}
|
|
300
|
+
catch (error) {
|
|
301
|
+
console.error(chalk.red(`Error: ${error.message}\n`));
|
|
302
|
+
process.exit(1);
|
|
303
|
+
}
|
|
304
|
+
});
|
|
305
|
+
team
|
|
306
|
+
.command('invite <email>')
|
|
307
|
+
.description('Invite team member')
|
|
308
|
+
.option('-r, --role <role>', 'Member role: viewer, member, admin', 'member')
|
|
309
|
+
.action(async (email, options) => {
|
|
310
|
+
try {
|
|
311
|
+
await inviteTeamMemberCommand(email, options);
|
|
312
|
+
}
|
|
313
|
+
catch (error) {
|
|
314
|
+
console.error(chalk.red(`Error: ${error.message}\n`));
|
|
315
|
+
process.exit(1);
|
|
316
|
+
}
|
|
317
|
+
});
|
|
318
|
+
team
|
|
319
|
+
.command('remove <email>')
|
|
320
|
+
.description('Remove team member')
|
|
321
|
+
.action(async (email) => {
|
|
322
|
+
try {
|
|
323
|
+
await removeTeamMemberCommand(email);
|
|
324
|
+
}
|
|
325
|
+
catch (error) {
|
|
326
|
+
console.error(chalk.red(`Error: ${error.message}\n`));
|
|
327
|
+
process.exit(1);
|
|
328
|
+
}
|
|
329
|
+
});
|
|
330
|
+
team
|
|
331
|
+
.command('update-role <email> <role>')
|
|
332
|
+
.description('Update member role (viewer, member, admin)')
|
|
333
|
+
.action(async (email, role) => {
|
|
334
|
+
try {
|
|
335
|
+
await updateTeamRoleCommand(email, role);
|
|
336
|
+
}
|
|
337
|
+
catch (error) {
|
|
338
|
+
console.error(chalk.red(`Error: ${error.message}\n`));
|
|
339
|
+
process.exit(1);
|
|
340
|
+
}
|
|
341
|
+
});
|
|
342
|
+
team
|
|
343
|
+
.command('invitations')
|
|
344
|
+
.description('List invitations')
|
|
345
|
+
.option('-p, --pending', 'Show only pending invitations')
|
|
346
|
+
.action(async (options) => {
|
|
347
|
+
try {
|
|
348
|
+
await listInvitationsCommand(options);
|
|
349
|
+
}
|
|
350
|
+
catch (error) {
|
|
351
|
+
console.error(chalk.red(`Error: ${error.message}\n`));
|
|
352
|
+
process.exit(1);
|
|
353
|
+
}
|
|
354
|
+
});
|
|
355
|
+
team
|
|
356
|
+
.command('revoke-invitation <email>')
|
|
357
|
+
.description('Revoke pending invitation')
|
|
358
|
+
.action(async (email) => {
|
|
359
|
+
try {
|
|
360
|
+
await revokeInvitationCommand(email);
|
|
361
|
+
}
|
|
362
|
+
catch (error) {
|
|
363
|
+
console.error(chalk.red(`Error: ${error.message}\n`));
|
|
364
|
+
process.exit(1);
|
|
365
|
+
}
|
|
366
|
+
});
|
|
367
|
+
// Organization command
|
|
368
|
+
const org = program
|
|
369
|
+
.command('org')
|
|
370
|
+
.description('Manage organization');
|
|
371
|
+
org
|
|
372
|
+
.command('info')
|
|
373
|
+
.description('Get organization information')
|
|
374
|
+
.action(async () => {
|
|
375
|
+
try {
|
|
376
|
+
await orgInfoCommand();
|
|
377
|
+
}
|
|
378
|
+
catch (error) {
|
|
379
|
+
console.error(chalk.red(`Error: ${error.message}\n`));
|
|
380
|
+
process.exit(1);
|
|
381
|
+
}
|
|
382
|
+
});
|
|
383
|
+
org
|
|
384
|
+
.command('stats')
|
|
385
|
+
.description('Get organization statistics')
|
|
386
|
+
.action(async () => {
|
|
387
|
+
try {
|
|
388
|
+
await orgStatsCommand();
|
|
389
|
+
}
|
|
390
|
+
catch (error) {
|
|
391
|
+
console.error(chalk.red(`Error: ${error.message}\n`));
|
|
392
|
+
process.exit(1);
|
|
393
|
+
}
|
|
394
|
+
});
|
|
395
|
+
org
|
|
396
|
+
.command('plan')
|
|
397
|
+
.description('Get subscription plan and limits')
|
|
398
|
+
.action(async () => {
|
|
399
|
+
try {
|
|
400
|
+
await orgPlanCommand();
|
|
401
|
+
}
|
|
402
|
+
catch (error) {
|
|
403
|
+
console.error(chalk.red(`Error: ${error.message}\n`));
|
|
404
|
+
process.exit(1);
|
|
405
|
+
}
|
|
406
|
+
});
|
|
407
|
+
// Pull command (deprecated, use export)
|
|
91
408
|
program
|
|
92
409
|
.command('pull <project>')
|
|
93
|
-
.description('Pull translations from a project (
|
|
410
|
+
.description('Pull translations from a project (deprecated, use export)')
|
|
94
411
|
.option('-l, --language <code>', 'Pull specific language only (default: all)')
|
|
95
412
|
.option('-f, --format <type>', 'Export format: json, json-nested, ios, android, flutter (default: json)')
|
|
96
413
|
.option('-d, --dir <path>', 'Output directory (default: ./translations)')
|
|
@@ -105,6 +422,40 @@ program
|
|
|
105
422
|
process.exit(1);
|
|
106
423
|
}
|
|
107
424
|
});
|
|
425
|
+
// Export command
|
|
426
|
+
program
|
|
427
|
+
.command('export <project>')
|
|
428
|
+
.description('Export translations from a project')
|
|
429
|
+
.option('-l, --language <code>', 'Export specific language (default: all)')
|
|
430
|
+
.option('-f, --format <type>', 'Export format: flat-json, nested-json, i18n-json, android-xml, ios-strings, flutter-arb (default: flat-json)')
|
|
431
|
+
.option('-o, --output <path>', 'Output file path')
|
|
432
|
+
.option('-m, --module <name>', 'Export only keys from specific module')
|
|
433
|
+
.option('--include-unpublished', 'Include unpublished translations')
|
|
434
|
+
.action(async (project, options) => {
|
|
435
|
+
try {
|
|
436
|
+
await exportCommand(project, options);
|
|
437
|
+
}
|
|
438
|
+
catch (error) {
|
|
439
|
+
console.error(chalk.red(`Error: ${error.message}\n`));
|
|
440
|
+
process.exit(1);
|
|
441
|
+
}
|
|
442
|
+
});
|
|
443
|
+
// Import command
|
|
444
|
+
program
|
|
445
|
+
.command('import <project> <file>')
|
|
446
|
+
.description('Import translations from a file')
|
|
447
|
+
.requiredOption('-l, --language <code>', 'Target language code')
|
|
448
|
+
.option('--overwrite', 'Overwrite existing translations')
|
|
449
|
+
.option('--publish', 'Auto-publish after import')
|
|
450
|
+
.action(async (project, file, options) => {
|
|
451
|
+
try {
|
|
452
|
+
await importCommand(project, file, options);
|
|
453
|
+
}
|
|
454
|
+
catch (error) {
|
|
455
|
+
console.error(chalk.red(`Error: ${error.message}\n`));
|
|
456
|
+
process.exit(1);
|
|
457
|
+
}
|
|
458
|
+
});
|
|
108
459
|
// Debug command
|
|
109
460
|
const debug = program
|
|
110
461
|
.command('debug')
|