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.
@@ -0,0 +1,150 @@
1
+ import chalk from 'chalk';
2
+ import ora from 'ora';
3
+ import { readFileSync } from 'fs';
4
+ import { resolve } from 'path';
5
+ import { isAuthenticated } from '../auth.js';
6
+ import { getApiClient } from '../api.js';
7
+ import { config } from '../config.js';
8
+ /**
9
+ * Parse translation file based on extension
10
+ */
11
+ function parseTranslationFile(filePath) {
12
+ const content = readFileSync(filePath, 'utf-8');
13
+ const ext = filePath.split('.').pop()?.toLowerCase();
14
+ if (ext === 'json') {
15
+ const parsed = JSON.parse(content);
16
+ // Check if it's nested JSON and flatten it
17
+ if (isNestedObject(parsed)) {
18
+ return flattenObject(parsed);
19
+ }
20
+ return parsed;
21
+ }
22
+ throw new Error(`Unsupported file format: .${ext}. Only JSON files are currently supported.`);
23
+ }
24
+ /**
25
+ * Check if object has nested objects
26
+ */
27
+ function isNestedObject(obj) {
28
+ return Object.values(obj).some(value => typeof value === 'object' && value !== null && !Array.isArray(value));
29
+ }
30
+ /**
31
+ * Flatten nested object to dot-notation keys
32
+ */
33
+ function flattenObject(obj, prefix = '') {
34
+ const result = {};
35
+ Object.entries(obj).forEach(([key, value]) => {
36
+ const fullKey = prefix ? `${prefix}.${key}` : key;
37
+ if (typeof value === 'string') {
38
+ result[fullKey] = value;
39
+ }
40
+ else if (typeof value === 'object' && value !== null) {
41
+ Object.assign(result, flattenObject(value, fullKey));
42
+ }
43
+ });
44
+ return result;
45
+ }
46
+ /**
47
+ * Import translations command
48
+ */
49
+ export async function importCommand(projectSlug, filePath, options) {
50
+ if (!isAuthenticated()) {
51
+ console.log(chalk.red('✗ Not authenticated. Please run "langctl auth <api-key>" first.\n'));
52
+ process.exitCode = 1;
53
+ return;
54
+ }
55
+ const orgId = config.get('organizationId');
56
+ if (!orgId) {
57
+ console.log(chalk.red('✗ Organization ID not found. Please run "langctl auth <api-key>" again.\n'));
58
+ process.exitCode = 1;
59
+ return;
60
+ }
61
+ const spinner = ora('Loading file...').start();
62
+ try {
63
+ // Resolve file path
64
+ const resolvedPath = resolve(filePath);
65
+ // Parse translation file
66
+ spinner.text = 'Parsing translation file...';
67
+ const translations = parseTranslationFile(resolvedPath);
68
+ const keyCount = Object.keys(translations).length;
69
+ if (keyCount === 0) {
70
+ spinner.warn(chalk.yellow('No translations found in file'));
71
+ return;
72
+ }
73
+ spinner.text = 'Fetching project...';
74
+ // Get project by slug
75
+ const api = getApiClient();
76
+ const project = await api.get(`/orgs/${orgId}/projects/by-slug/${projectSlug}`);
77
+ // Validate language
78
+ if (!project.languages.includes(options.language)) {
79
+ spinner.fail(chalk.red(`Language "${options.language}" not found in project`));
80
+ console.log(chalk.yellow(`Available languages: ${project.languages.join(', ')}\n`));
81
+ return;
82
+ }
83
+ spinner.text = `Importing ${keyCount} translations...`;
84
+ // Push translations via REST API
85
+ const body = {
86
+ language: options.language,
87
+ translations,
88
+ // Keep import safe by default; only overwrite when explicitly requested.
89
+ overwriteExisting: options.overwrite === true
90
+ };
91
+ if (options.module)
92
+ body.module = options.module;
93
+ const data = await api.post(`/orgs/${orgId}/projects/${project.id}/import`, body);
94
+ let publishedCount = 0;
95
+ if (options.publish) {
96
+ spinner.text = 'Publishing imported keys...';
97
+ const keyNames = new Set(Object.keys(translations));
98
+ const keyIds = [];
99
+ let page = 1;
100
+ const pageSize = 100;
101
+ while (true) {
102
+ const keysPage = await api.get(`/orgs/${orgId}/projects/${project.id}/keys`, {
103
+ page: String(page),
104
+ pageSize: String(pageSize)
105
+ });
106
+ for (const key of (keysPage.data || [])) {
107
+ if (keyNames.has(key.key)) {
108
+ keyIds.push(key.id);
109
+ }
110
+ }
111
+ const totalPages = keysPage.pagination?.totalPages || 1;
112
+ if (page >= totalPages)
113
+ break;
114
+ page += 1;
115
+ }
116
+ if (keyIds.length > 0) {
117
+ await api.post(`/orgs/${orgId}/projects/${project.id}/keys/bulk-publish`, {
118
+ keyIds,
119
+ published: true
120
+ });
121
+ publishedCount = keyIds.length;
122
+ }
123
+ }
124
+ spinner.succeed(chalk.green('Import completed successfully'));
125
+ console.log(chalk.green(`\n✓ Created: ${data.created}`));
126
+ console.log(chalk.blue(`✓ Updated: ${data.updated}`));
127
+ console.log(chalk.yellow(`⊘ Skipped: ${data.skipped}`));
128
+ if (options.publish) {
129
+ console.log(chalk.magenta(`✓ Published: ${publishedCount}`));
130
+ }
131
+ console.log(chalk.white(` Total: ${data.total}`));
132
+ console.log('');
133
+ }
134
+ catch (error) {
135
+ spinner.fail(chalk.red('Failed to import translations'));
136
+ if (error.code === 'ENOENT') {
137
+ console.error(chalk.red(`Error: File not found: ${filePath}\n`));
138
+ process.exitCode = 1;
139
+ }
140
+ else if (error instanceof SyntaxError) {
141
+ console.error(chalk.red(`Error: Invalid JSON file\n`));
142
+ process.exitCode = 1;
143
+ }
144
+ else {
145
+ console.error(chalk.red(`Error: ${error.message}\n`));
146
+ process.exitCode = 1;
147
+ }
148
+ }
149
+ }
150
+ //# sourceMappingURL=import.js.map
@@ -3,7 +3,6 @@ import chalk from 'chalk';
3
3
  import ora from 'ora';
4
4
  import { config } from '../config.js';
5
5
  import { authenticate } from '../auth.js';
6
- import { initializeSupabase } from '../supabase.js';
7
6
  import { showLogo } from '../utils/banner.js';
8
7
  export async function initCommand() {
9
8
  // Show logo
@@ -25,15 +24,6 @@ export async function initCommand() {
25
24
  return;
26
25
  }
27
26
  }
28
- // Initialize Supabase client (with hardcoded credentials - user doesn't need to know)
29
- try {
30
- initializeSupabase();
31
- }
32
- catch (error) {
33
- console.error(chalk.red('✗ Failed to connect to Langctl servers'));
34
- console.error(chalk.red('Please check your internet connection and try again.\n'));
35
- return;
36
- }
37
27
  // Ask for API key
38
28
  const { apiKey } = await inquirer.prompt([
39
29
  {
@@ -0,0 +1,25 @@
1
+ /**
2
+ * List translation keys
3
+ */
4
+ export declare function listKeysCommand(projectSlug: string, options: any): Promise<void>;
5
+ /**
6
+ * Get single key
7
+ */
8
+ export declare function getKeyCommand(projectSlug: string, keyName: string): Promise<void>;
9
+ /**
10
+ * Create translation key
11
+ */
12
+ export declare function createKeyCommand(projectSlug: string, keyName: string, options: any): Promise<void>;
13
+ /**
14
+ * Delete translation key
15
+ */
16
+ export declare function deleteKeyCommand(projectSlug: string, keyName: string): Promise<void>;
17
+ /**
18
+ * Update single translation
19
+ */
20
+ export declare function translateKeyCommand(projectSlug: string, keyName: string, options: any): Promise<void>;
21
+ /**
22
+ * Publish/unpublish keys
23
+ */
24
+ export declare function publishKeysCommand(projectSlug: string, keys: string[], options: any): Promise<void>;
25
+ //# sourceMappingURL=keys.d.ts.map
@@ -0,0 +1,294 @@
1
+ import chalk from 'chalk';
2
+ import ora from 'ora';
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
+ * Find a key by exact name within a project (search + exact match)
15
+ */
16
+ async function findKeyByName(orgId, projectId, keyName) {
17
+ const api = getApiClient();
18
+ const result = await api.get(`/orgs/${orgId}/projects/${projectId}/keys`, {
19
+ search: keyName,
20
+ pageSize: '100'
21
+ });
22
+ const key = result.data?.find((k) => k.key === keyName);
23
+ return key || null;
24
+ }
25
+ /**
26
+ * List translation keys
27
+ */
28
+ export async function listKeysCommand(projectSlug, options) {
29
+ if (!isAuthenticated()) {
30
+ console.log(chalk.red('✗ Not authenticated. Please run "langctl auth <api-key>" first.\n'));
31
+ process.exitCode = 1;
32
+ return;
33
+ }
34
+ const orgId = config.get('organizationId');
35
+ if (!orgId) {
36
+ console.log(chalk.red('✗ Organization ID not found. Please run "langctl auth <api-key>" again.\n'));
37
+ process.exitCode = 1;
38
+ return;
39
+ }
40
+ const spinner = ora('Fetching keys...').start();
41
+ try {
42
+ const project = await resolveProject(orgId, projectSlug);
43
+ const limit = Math.max(1, parseInt(options.limit || '100', 10) || 100);
44
+ const offset = Math.max(0, parseInt(options.offset || '0', 10) || 0);
45
+ const skipWithinPage = offset % limit;
46
+ const page = Math.floor(offset / limit) + 1;
47
+ const pageSize = Math.min(100, limit + skipWithinPage);
48
+ const api = getApiClient();
49
+ const params = {
50
+ page: String(page),
51
+ pageSize: String(pageSize)
52
+ };
53
+ if (options.module)
54
+ params.module = options.module;
55
+ if (options.search)
56
+ params.search = options.search;
57
+ if (options.published)
58
+ params.published = 'true';
59
+ const result = await api.get(`/orgs/${orgId}/projects/${project.id}/keys`, params);
60
+ spinner.stop();
61
+ let keys = result.data || [];
62
+ if (skipWithinPage > 0) {
63
+ keys = keys.slice(skipWithinPage);
64
+ }
65
+ keys = keys.slice(0, limit);
66
+ const total = result.pagination?.total;
67
+ if (!keys || keys.length === 0) {
68
+ console.log(chalk.yellow('\nNo keys found\n'));
69
+ return;
70
+ }
71
+ console.log(chalk.blue.bold(`\n🔑 Translation Keys (${keys.length}${total ? ` of ${total}` : ''})\n`));
72
+ keys.forEach((key) => {
73
+ console.log(chalk.white.bold(key.key));
74
+ if (key.description) {
75
+ console.log(chalk.gray(` Description: ${key.description}`));
76
+ }
77
+ if (key.module) {
78
+ console.log(chalk.gray(` Module: ${key.module}`));
79
+ }
80
+ console.log(chalk.gray(` Published: ${key.published ? chalk.green('Yes') : chalk.yellow('No')}`));
81
+ const langs = Object.keys(key.translations || {});
82
+ if (langs.length > 0) {
83
+ console.log(chalk.gray(` Languages: ${langs.join(', ')}`));
84
+ }
85
+ console.log('');
86
+ });
87
+ }
88
+ catch (error) {
89
+ spinner.fail(chalk.red('Failed to list keys'));
90
+ console.error(chalk.red(`Error: ${error.message}\n`));
91
+ process.exitCode = 1;
92
+ }
93
+ }
94
+ /**
95
+ * Get single key
96
+ */
97
+ export async function getKeyCommand(projectSlug, keyName) {
98
+ if (!isAuthenticated()) {
99
+ console.log(chalk.red('✗ Not authenticated. Please run "langctl auth <api-key>" first.\n'));
100
+ process.exitCode = 1;
101
+ return;
102
+ }
103
+ const orgId = config.get('organizationId');
104
+ if (!orgId) {
105
+ console.log(chalk.red('✗ Organization ID not found. Please run "langctl auth <api-key>" again.\n'));
106
+ process.exitCode = 1;
107
+ return;
108
+ }
109
+ const spinner = ora('Fetching key...').start();
110
+ try {
111
+ const project = await resolveProject(orgId, projectSlug);
112
+ const key = await findKeyByName(orgId, project.id, keyName);
113
+ if (!key) {
114
+ spinner.fail(chalk.red(`Key "${keyName}" not found`));
115
+ return;
116
+ }
117
+ spinner.stop();
118
+ console.log(chalk.blue.bold('\n🔑 Translation Key Details\n'));
119
+ console.log(chalk.white.bold(`Key: ${key.key}`));
120
+ console.log(chalk.gray(`ID: ${key.id}`));
121
+ if (key.description) {
122
+ console.log(chalk.gray(`Description: ${key.description}`));
123
+ }
124
+ if (key.module) {
125
+ console.log(chalk.gray(`Module: ${key.module}`));
126
+ }
127
+ console.log(chalk.gray(`Published: ${key.published ? chalk.green('Yes') : chalk.yellow('No')}`));
128
+ console.log(chalk.blue.bold('\nTranslations:'));
129
+ Object.entries(key.translations || {}).forEach(([lang, value]) => {
130
+ console.log(chalk.white(` ${lang}: ${value}`));
131
+ });
132
+ console.log('');
133
+ }
134
+ catch (error) {
135
+ spinner.fail(chalk.red('Failed to get key'));
136
+ console.error(chalk.red(`Error: ${error.message}\n`));
137
+ process.exitCode = 1;
138
+ }
139
+ }
140
+ /**
141
+ * Create translation key
142
+ */
143
+ export async function createKeyCommand(projectSlug, keyName, options) {
144
+ if (!isAuthenticated()) {
145
+ console.log(chalk.red('✗ Not authenticated. Please run "langctl auth <api-key>" first.\n'));
146
+ process.exitCode = 1;
147
+ return;
148
+ }
149
+ const orgId = config.get('organizationId');
150
+ if (!orgId) {
151
+ console.log(chalk.red('✗ Organization ID not found. Please run "langctl auth <api-key>" again.\n'));
152
+ process.exitCode = 1;
153
+ return;
154
+ }
155
+ const spinner = ora('Creating key...').start();
156
+ try {
157
+ const project = await resolveProject(orgId, projectSlug);
158
+ // Build translations object from --value-* options
159
+ const translations = {};
160
+ Object.keys(options).forEach(opt => {
161
+ if (opt.startsWith('value')) {
162
+ const lang = opt.replace('value', '').toLowerCase();
163
+ if (lang) {
164
+ translations[lang] = options[opt];
165
+ }
166
+ }
167
+ });
168
+ const api = getApiClient();
169
+ await api.post(`/orgs/${orgId}/projects/${project.id}/keys`, {
170
+ key: keyName,
171
+ translations: Object.keys(translations).length > 0 ? translations : undefined,
172
+ description: options.description,
173
+ module: options.module,
174
+ tags: options.tags ? options.tags.split(',') : undefined
175
+ });
176
+ spinner.succeed(chalk.green(`Created key: ${keyName}`));
177
+ console.log('');
178
+ }
179
+ catch (error) {
180
+ spinner.fail(chalk.red('Failed to create key'));
181
+ console.error(chalk.red(`Error: ${error.message}\n`));
182
+ process.exitCode = 1;
183
+ }
184
+ }
185
+ /**
186
+ * Delete translation key
187
+ */
188
+ export async function deleteKeyCommand(projectSlug, keyName) {
189
+ if (!isAuthenticated()) {
190
+ console.log(chalk.red('✗ Not authenticated. Please run "langctl auth <api-key>" first.\n'));
191
+ process.exitCode = 1;
192
+ return;
193
+ }
194
+ const orgId = config.get('organizationId');
195
+ if (!orgId) {
196
+ console.log(chalk.red('✗ Organization ID not found. Please run "langctl auth <api-key>" again.\n'));
197
+ process.exitCode = 1;
198
+ return;
199
+ }
200
+ const spinner = ora('Deleting key...').start();
201
+ try {
202
+ const project = await resolveProject(orgId, projectSlug);
203
+ const key = await findKeyByName(orgId, project.id, keyName);
204
+ if (!key) {
205
+ spinner.fail(chalk.red(`Key "${keyName}" not found`));
206
+ return;
207
+ }
208
+ const api = getApiClient();
209
+ await api.delete(`/orgs/${orgId}/projects/${project.id}/keys/${key.id}`);
210
+ spinner.succeed(chalk.green(`Deleted key: ${keyName}`));
211
+ console.log('');
212
+ }
213
+ catch (error) {
214
+ spinner.fail(chalk.red('Failed to delete key'));
215
+ console.error(chalk.red(`Error: ${error.message}\n`));
216
+ process.exitCode = 1;
217
+ }
218
+ }
219
+ /**
220
+ * Update single translation
221
+ */
222
+ export async function translateKeyCommand(projectSlug, keyName, options) {
223
+ if (!isAuthenticated()) {
224
+ console.log(chalk.red('✗ Not authenticated. Please run "langctl auth <api-key>" first.\n'));
225
+ process.exitCode = 1;
226
+ return;
227
+ }
228
+ const orgId = config.get('organizationId');
229
+ if (!orgId) {
230
+ console.log(chalk.red('✗ Organization ID not found. Please run "langctl auth <api-key>" again.\n'));
231
+ process.exitCode = 1;
232
+ return;
233
+ }
234
+ const spinner = ora('Updating translation...').start();
235
+ try {
236
+ const project = await resolveProject(orgId, projectSlug);
237
+ const key = await findKeyByName(orgId, project.id, keyName);
238
+ if (!key) {
239
+ spinner.fail(chalk.red(`Key "${keyName}" not found`));
240
+ return;
241
+ }
242
+ const api = getApiClient();
243
+ await api.patch(`/orgs/${orgId}/projects/${project.id}/keys/${key.id}/translations/${options.language}`, { value: options.value });
244
+ spinner.succeed(chalk.green(`Updated ${options.language} translation for ${keyName}`));
245
+ console.log('');
246
+ }
247
+ catch (error) {
248
+ spinner.fail(chalk.red('Failed to update translation'));
249
+ console.error(chalk.red(`Error: ${error.message}\n`));
250
+ process.exitCode = 1;
251
+ }
252
+ }
253
+ /**
254
+ * Publish/unpublish keys
255
+ */
256
+ export async function publishKeysCommand(projectSlug, keys, options) {
257
+ if (!isAuthenticated()) {
258
+ console.log(chalk.red('✗ Not authenticated. Please run "langctl auth <api-key>" first.\n'));
259
+ process.exitCode = 1;
260
+ return;
261
+ }
262
+ const orgId = config.get('organizationId');
263
+ if (!orgId) {
264
+ console.log(chalk.red('✗ Organization ID not found. Please run "langctl auth <api-key>" again.\n'));
265
+ process.exitCode = 1;
266
+ return;
267
+ }
268
+ const action = options.unpublish ? 'unpublish' : 'publish';
269
+ const spinner = ora(`${action === 'publish' ? 'Publishing' : 'Unpublishing'} keys...`).start();
270
+ try {
271
+ const project = await resolveProject(orgId, projectSlug);
272
+ // List all keys to match names to IDs
273
+ const api = getApiClient();
274
+ const result = await api.get(`/orgs/${orgId}/projects/${project.id}/keys`, {
275
+ pageSize: '1000'
276
+ });
277
+ const keyIds = result.data
278
+ ?.filter((k) => keys.includes(k.key))
279
+ .map((k) => k.id) || [];
280
+ if (keyIds.length === 0) {
281
+ spinner.fail(chalk.red('No matching keys found'));
282
+ return;
283
+ }
284
+ const response = await api.post(`/orgs/${orgId}/projects/${project.id}/keys/bulk-publish`, { keyIds, published: !options.unpublish });
285
+ spinner.succeed(chalk.green(response.message || `${action === 'publish' ? 'Published' : 'Unpublished'} ${response.count} key(s)`));
286
+ console.log('');
287
+ }
288
+ catch (error) {
289
+ spinner.fail(chalk.red(`Failed to ${action} keys`));
290
+ console.error(chalk.red(`Error: ${error.message}\n`));
291
+ process.exitCode = 1;
292
+ }
293
+ }
294
+ //# sourceMappingURL=keys.js.map
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Get organization information
3
+ */
4
+ export declare function orgInfoCommand(): Promise<void>;
5
+ /**
6
+ * Get organization statistics
7
+ */
8
+ export declare function orgStatsCommand(): Promise<void>;
9
+ /**
10
+ * Get organization plan and limits
11
+ */
12
+ export declare function orgPlanCommand(): Promise<void>;
13
+ //# sourceMappingURL=org.d.ts.map
@@ -0,0 +1,118 @@
1
+ import chalk from 'chalk';
2
+ import ora from 'ora';
3
+ import { isAuthenticated } from '../auth.js';
4
+ import { getApiClient } from '../api.js';
5
+ import { config } from '../config.js';
6
+ /**
7
+ * Get organization information
8
+ */
9
+ export async function orgInfoCommand() {
10
+ if (!isAuthenticated()) {
11
+ console.log(chalk.red('✗ Not authenticated. Please run "langctl auth <api-key>" first.\n'));
12
+ process.exitCode = 1;
13
+ return;
14
+ }
15
+ const orgId = config.get('organizationId');
16
+ if (!orgId) {
17
+ console.log(chalk.red('✗ No organization configured. Please run "langctl init" first.\n'));
18
+ process.exitCode = 1;
19
+ return;
20
+ }
21
+ const spinner = ora('Fetching organization info...').start();
22
+ try {
23
+ const api = getApiClient();
24
+ const org = await api.get(`/orgs/${orgId}`);
25
+ spinner.stop();
26
+ console.log(chalk.blue.bold('\n🏢 Organization Information\n'));
27
+ console.log(chalk.white.bold(org.name));
28
+ console.log(chalk.gray(`ID: ${org.id}`));
29
+ console.log(chalk.gray(`Slug: ${org.slug}`));
30
+ console.log(chalk.gray(`Plan: ${org.plan}`));
31
+ console.log(chalk.gray(`Created: ${new Date(org.createdAt).toLocaleDateString()}`));
32
+ console.log('');
33
+ }
34
+ catch (error) {
35
+ spinner.fail(chalk.red('Failed to fetch organization info'));
36
+ console.error(chalk.red(`Error: ${error.message}\n`));
37
+ process.exitCode = 1;
38
+ }
39
+ }
40
+ /**
41
+ * Get organization statistics
42
+ */
43
+ export async function orgStatsCommand() {
44
+ if (!isAuthenticated()) {
45
+ console.log(chalk.red('✗ Not authenticated. Please run "langctl auth <api-key>" first.\n'));
46
+ process.exitCode = 1;
47
+ return;
48
+ }
49
+ const orgId = config.get('organizationId');
50
+ if (!orgId) {
51
+ console.log(chalk.red('✗ No organization configured. Please run "langctl init" first.\n'));
52
+ process.exitCode = 1;
53
+ return;
54
+ }
55
+ const spinner = ora('Fetching organization statistics...').start();
56
+ try {
57
+ const api = getApiClient();
58
+ const stats = await api.get(`/orgs/${orgId}/stats`);
59
+ spinner.stop();
60
+ console.log(chalk.blue.bold('\n📊 Organization Statistics\n'));
61
+ console.log(chalk.white.bold('Team'));
62
+ console.log(chalk.gray(` Members: ${stats.memberCount}`));
63
+ console.log('');
64
+ console.log(chalk.white.bold('Projects'));
65
+ console.log(chalk.gray(` Total Projects: ${stats.projectCount}`));
66
+ console.log('');
67
+ console.log(chalk.white.bold('Translation Keys'));
68
+ console.log(chalk.gray(` Total Keys: ${stats.totalKeys}`));
69
+ console.log('');
70
+ console.log(chalk.white.bold('Resources'));
71
+ console.log(chalk.gray(` API Keys: ${stats.apiKeyCount}`));
72
+ console.log('');
73
+ }
74
+ catch (error) {
75
+ spinner.fail(chalk.red('Failed to fetch statistics'));
76
+ console.error(chalk.red(`Error: ${error.message}\n`));
77
+ process.exitCode = 1;
78
+ }
79
+ }
80
+ /**
81
+ * Get organization plan and limits
82
+ */
83
+ export async function orgPlanCommand() {
84
+ if (!isAuthenticated()) {
85
+ console.log(chalk.red('✗ Not authenticated. Please run "langctl auth <api-key>" first.\n'));
86
+ process.exitCode = 1;
87
+ return;
88
+ }
89
+ const orgId = config.get('organizationId');
90
+ if (!orgId) {
91
+ console.log(chalk.red('✗ No organization configured. Please run "langctl init" first.\n'));
92
+ process.exitCode = 1;
93
+ return;
94
+ }
95
+ const spinner = ora('Fetching plan information...').start();
96
+ try {
97
+ const api = getApiClient();
98
+ const org = await api.get(`/orgs/${orgId}`);
99
+ spinner.stop();
100
+ console.log(chalk.blue.bold('\n💎 Subscription Plan\n'));
101
+ console.log(chalk.white.bold(`Current Plan: ${org.plan.toUpperCase()}`));
102
+ console.log('');
103
+ console.log(chalk.white.bold('Plan Limits'));
104
+ // Display limits with "Unlimited" for -1 or null values
105
+ const formatLimit = (value) => (value === -1 || value === null) ? chalk.green('Unlimited') : chalk.gray(value.toString());
106
+ console.log(chalk.gray(` Max Members: ${formatLimit(org.maxMembers)}`));
107
+ console.log(chalk.gray(` Max Projects: ${formatLimit(org.maxProjects)}`));
108
+ console.log(chalk.gray(` Max Keys per Project: ${formatLimit(org.maxKeysPerProject)}`));
109
+ console.log(chalk.gray(` Max API Keys: ${formatLimit(org.maxApiKeys)}`));
110
+ console.log('');
111
+ }
112
+ catch (error) {
113
+ spinner.fail(chalk.red('Failed to fetch plan information'));
114
+ console.error(chalk.red(`Error: ${error.message}\n`));
115
+ process.exitCode = 1;
116
+ }
117
+ }
118
+ //# sourceMappingURL=org.js.map
@@ -1,2 +1,33 @@
1
+ /**
2
+ * List all projects
3
+ */
1
4
  export declare function projectsListCommand(): Promise<void>;
5
+ /**
6
+ * Create new project
7
+ */
8
+ export declare function projectsCreateCommand(name: string, options: any): Promise<void>;
9
+ /**
10
+ * Get project details
11
+ */
12
+ export declare function projectsGetCommand(slug: string): Promise<void>;
13
+ /**
14
+ * Update project
15
+ */
16
+ export declare function projectsUpdateCommand(slug: string, options: any): Promise<void>;
17
+ /**
18
+ * Delete project
19
+ */
20
+ export declare function projectsDeleteCommand(slug: string): Promise<void>;
21
+ /**
22
+ * Add language to project
23
+ */
24
+ export declare function projectsAddLanguageCommand(slug: string, language: string): Promise<void>;
25
+ /**
26
+ * Remove language from project
27
+ */
28
+ export declare function projectsRemoveLanguageCommand(slug: string, language: string): Promise<void>;
29
+ /**
30
+ * Get project statistics
31
+ */
32
+ export declare function projectsStatsCommand(slug: string): Promise<void>;
2
33
  //# sourceMappingURL=projects.d.ts.map