langctl 0.1.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/LICENSE +1 -1
  3. package/README.md +159 -1151
  4. package/dist/commands/auth.js +82 -24
  5. package/dist/commands/config.js +36 -25
  6. package/dist/commands/export.js +11 -112
  7. package/dist/commands/import.js +32 -164
  8. package/dist/commands/init.js +59 -80
  9. package/dist/commands/keys.js +145 -381
  10. package/dist/commands/org.js +45 -140
  11. package/dist/commands/projects.js +125 -390
  12. package/dist/commands/pull.js +78 -183
  13. package/dist/commands/push.js +125 -0
  14. package/dist/commands/team.js +68 -272
  15. package/dist/core/config.js +130 -0
  16. package/dist/core/errors.js +67 -0
  17. package/dist/core/files.js +38 -0
  18. package/dist/core/http.js +121 -0
  19. package/dist/core/output.js +96 -0
  20. package/dist/core/project.js +58 -0
  21. package/dist/core/prompts.js +56 -0
  22. package/dist/formats/index.js +285 -0
  23. package/dist/index.js +261 -508
  24. package/dist/version.js +4 -0
  25. package/package.json +22 -16
  26. package/.gitattributes +0 -2
  27. package/NEXT_PHASE.md +0 -618
  28. package/WORKING_INSTRUCTIONS.md +0 -1389
  29. package/dist/auth.d.ts +0 -37
  30. package/dist/auth.js +0 -112
  31. package/dist/commands/auth.d.ts +0 -3
  32. package/dist/commands/config.d.ts +0 -2
  33. package/dist/commands/debug.d.ts +0 -3
  34. package/dist/commands/debug.js +0 -57
  35. package/dist/commands/export.d.ts +0 -14
  36. package/dist/commands/import.d.ts +0 -11
  37. package/dist/commands/init.d.ts +0 -2
  38. package/dist/commands/keys.d.ts +0 -25
  39. package/dist/commands/org.d.ts +0 -13
  40. package/dist/commands/projects.d.ts +0 -30
  41. package/dist/commands/pull.d.ts +0 -9
  42. package/dist/commands/team.d.ts +0 -29
  43. package/dist/config.d.ts +0 -47
  44. package/dist/config.js +0 -69
  45. package/dist/exporters/index.d.ts +0 -36
  46. package/dist/exporters/index.js +0 -214
  47. package/dist/index.d.ts +0 -3
  48. package/dist/supabase.d.ts +0 -14
  49. package/dist/supabase.js +0 -30
  50. package/dist/utils/banner.d.ts +0 -9
  51. package/dist/utils/banner.js +0 -24
  52. package/translations/en.json +0 -5
  53. package/translations/en.xml +0 -9
  54. package/translations/fr.json +0 -4
  55. package/translations/fr.xml +0 -7
  56. package/translations/hi.json +0 -4
  57. package/translations/hi.xml +0 -7
  58. package/translations/ios/en.strings +0 -9
  59. package/translations/ios/fr.strings +0 -6
  60. package/translations/ios/hi.strings +0 -6
  61. package/translations/ios/ja.strings +0 -6
  62. package/translations/ja.json +0 -4
  63. package/translations/ja.xml +0 -7
@@ -1,87 +1,66 @@
1
- import inquirer from 'inquirer';
2
1
  import chalk from 'chalk';
3
- import ora from 'ora';
4
- import { config } from '../config.js';
5
- import { authenticate } from '../auth.js';
6
- import { initializeSupabase } from '../supabase.js';
7
- import { showLogo } from '../utils/banner.js';
8
- export async function initCommand() {
9
- // Show logo
10
- showLogo();
11
- console.log(chalk.blue.bold('šŸš€ CLI Setup\n'));
12
- console.log(chalk.gray('Get your API key from: https://app.langctl.com/dashboard/settings/api-keys\n'));
13
- // Check if already configured
14
- if (config.isConfigured()) {
15
- const { confirm } = await inquirer.prompt([
16
- {
17
- type: 'confirm',
18
- name: 'confirm',
19
- message: 'Configuration already exists. Do you want to reconfigure?',
20
- default: false
21
- }
22
- ]);
23
- if (!confirm) {
24
- console.log(chalk.yellow('\nSetup cancelled.'));
25
- return;
26
- }
2
+ import { existsSync } from 'fs';
3
+ import { join } from 'path';
4
+ import { PROJECT_CONFIG_FILE, resolveCredentials, writeProjectConfig } from '../core/config.js';
5
+ import { usageError } from '../core/errors.js';
6
+ import { getSession } from '../core/http.js';
7
+ import { isInteractive, log, printJson, runtime } from '../core/output.js';
8
+ import { input, password } from '../core/prompts.js';
9
+ import { FORMATS, getFormat } from '../formats/index.js';
10
+ import { saveApiKey } from './auth.js';
11
+ /**
12
+ * Set up a repository: authenticate if needed, then write langctl.json so that
13
+ * `langctl pull` / `langctl push` need no arguments (locally and in CI).
14
+ */
15
+ export async function initCommand(opts) {
16
+ const interactive = isInteractive();
17
+ const target = join(process.cwd(), PROJECT_CONFIG_FILE);
18
+ if (existsSync(target) && !opts.force) {
19
+ throw usageError(`${PROJECT_CONFIG_FILE} already exists here.`, 'Edit it directly, or re-run with --force to replace it.');
27
20
  }
28
- // Initialize Supabase client (with hardcoded credentials - user doesn't need to know)
29
- try {
30
- initializeSupabase();
21
+ if (!resolveCredentials()) {
22
+ if (!interactive)
23
+ throw usageError('Not authenticated.', 'Set LANGCTL_API_KEY, or run "langctl auth --stdin" first.');
24
+ log.info(`Create an API key at ${chalk.cyan('https://app.langctl.com/organization/api-keys')}`);
25
+ const org = await saveApiKey(await password('Paste your API key'));
26
+ log.success(`Authenticated to ${chalk.bold(org.name)}`);
31
27
  }
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;
28
+ const session = await getSession();
29
+ const projects = await session.api.get(`/orgs/${session.orgId}/projects`);
30
+ if (projects.length === 0) {
31
+ throw usageError('This organization has no projects yet.', 'Create one with "langctl projects create <name>" or at https://app.langctl.com.');
36
32
  }
37
- // Ask for API key
38
- const { apiKey } = await inquirer.prompt([
39
- {
40
- type: 'password',
41
- name: 'apiKey',
42
- message: 'Enter your API key:',
43
- mask: '*',
44
- filter: (input) => input?.trim(),
45
- validate: (input) => {
46
- const value = (input || '').trim();
47
- if (!value || !value.startsWith('lc_')) {
48
- return 'Please enter a valid API key (starts with lc_)';
49
- }
50
- if (value.length !== 67) {
51
- return 'Invalid API key format (should be 67 characters)';
52
- }
53
- return true;
54
- }
55
- }
56
- ]);
57
- // Authenticate
58
- const spinner = ora('Verifying API key...').start();
59
- const result = await authenticate(apiKey);
60
- if (result.success) {
61
- spinner.succeed(chalk.green(`Authenticated as ${result.data?.organization.name}`));
33
+ let project = opts.project ? projects.find(p => p.slug === opts.project) : undefined;
34
+ if (opts.project && !project) {
35
+ throw usageError(`Project "${opts.project}" not found.`, `Available: ${projects.map(p => p.slug).join(', ')}`);
62
36
  }
63
- else {
64
- spinner.fail(chalk.red(result.message));
65
- console.log(chalk.yellow('\nTroubleshooting:'));
66
- console.log(chalk.white(' - Check that your API key is correct'));
67
- console.log(chalk.white(' - Verify the key is not revoked in the dashboard'));
68
- console.log(chalk.white(' - Generate a new key if needed: https://app.langctl.com/dashboard/settings/api-keys\n'));
69
- return;
37
+ if (!project) {
38
+ if (projects.length === 1)
39
+ project = projects[0];
40
+ else if (!interactive)
41
+ throw usageError('Several projects exist — choose one with --project <slug>.', `Available: ${projects.map(p => p.slug).join(', ')}`);
42
+ else
43
+ project = await choose('Project', projects.map(p => ({ label: `${p.name} ${chalk.dim(`(${p.slug})`)}`, value: p })));
44
+ }
45
+ let format = opts.format ? getFormat(opts.format) : undefined;
46
+ if (!format) {
47
+ format = interactive ? await choose('Format', FORMATS.map(f => ({ label: f.label, value: f }))) : getFormat('json');
48
+ }
49
+ const output = opts.output ?? (interactive ? await input('Where should translation files go', format.defaultOutput) : format.defaultOutput);
50
+ const config = { project: project.slug, format: format.id, output };
51
+ writeProjectConfig(target, config);
52
+ if (runtime.json)
53
+ return printJson({ created: target, config });
54
+ log.success(`Wrote ${PROJECT_CONFIG_FILE} for ${chalk.bold(project.name)} (${project.languages.join(', ')})`);
55
+ log.info(`\nNext:\n ${chalk.cyan('langctl pull')} download translations into ${output}\n ${chalk.cyan('langctl push')} upload new ${project.defaultLanguage} strings\n ${chalk.cyan('langctl pull --check')} fail CI when committed files are out of date`);
56
+ }
57
+ async function choose(label, options) {
58
+ options.forEach((o, i) => log.info(` ${chalk.cyan(String(i + 1).padStart(2))} ${o.label}`));
59
+ for (;;) {
60
+ const answer = await input(`${label} [1-${options.length}]`, '1');
61
+ const n = Number(answer);
62
+ if (Number.isInteger(n) && n >= 1 && n <= options.length)
63
+ return options[n - 1].value;
64
+ log.warn(`Enter a number between 1 and ${options.length}.`);
70
65
  }
71
- // Ask for default settings
72
- const { defaultLanguage } = await inquirer.prompt([
73
- {
74
- type: 'input',
75
- name: 'defaultLanguage',
76
- message: 'Default language code:',
77
- default: 'en'
78
- }
79
- ]);
80
- config.set('defaultLanguage', defaultLanguage);
81
- console.log(chalk.green.bold('\nāœ“ Setup complete!\n'));
82
- console.log(chalk.gray(`Configuration saved to: ${config.getConfigPath()}\n`));
83
- console.log(chalk.blue('Next steps:'));
84
- console.log(chalk.white(' 1. Run "langctl projects list" to see your projects'));
85
- console.log(chalk.white(' 2. Run "langctl pull <project-slug>" to download translations\n'));
86
66
  }
87
- //# sourceMappingURL=init.js.map