lingo.dev 0.79.5 → 0.80.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/build/cli.cjs CHANGED
@@ -158,7 +158,8 @@ function createAuthenticator(params) {
158
158
  return null;
159
159
  }
160
160
  return {
161
- email: payload.email
161
+ email: payload.email,
162
+ id: payload.id
162
163
  };
163
164
  }
164
165
  return null;
@@ -988,7 +989,6 @@ var show_default = new (0, _interactivecommander.Command)().command("show").desc
988
989
 
989
990
  // src/cli/cmd/i18n.ts
990
991
 
991
- var __sdk = require('@lingo.dev/_sdk');
992
992
 
993
993
 
994
994
 
@@ -2742,7 +2742,8 @@ function variableExtractLoader(params) {
2742
2742
  return createLoader({
2743
2743
  pull: async (locale, input2) => {
2744
2744
  const result = {};
2745
- for (const [key, value] of Object.entries(input2)) {
2745
+ const inputValues = _lodash2.default.omitBy(input2, _lodash2.default.isEmpty);
2746
+ for (const [key, value] of Object.entries(inputValues)) {
2746
2747
  const matches = value.match(specifierPattern) || [];
2747
2748
  result[key] = result[key] || {
2748
2749
  value,
@@ -3057,8 +3058,8 @@ function createBucketLoader(bucketType, bucketPathPattern, options) {
3057
3058
  createPoLoader(),
3058
3059
  createFlatLoader(),
3059
3060
  createSyncLoader(),
3060
- createUnlocalizableLoader(options.isCacheRestore, options.returnUnlocalizedKeys),
3061
- createVariableLoader({ type: "python" })
3061
+ createVariableLoader({ type: "python" }),
3062
+ createUnlocalizableLoader(options.isCacheRestore, options.returnUnlocalizedKeys)
3062
3063
  );
3063
3064
  case "properties":
3064
3065
  return composeLoaders(
@@ -3090,8 +3091,8 @@ function createBucketLoader(bucketType, bucketPathPattern, options) {
3090
3091
  createXcodeXcstringsLoader(options.defaultLocale),
3091
3092
  createFlatLoader(),
3092
3093
  createSyncLoader(),
3093
- createUnlocalizableLoader(options.isCacheRestore, options.returnUnlocalizedKeys),
3094
- createVariableLoader({ type: "ieee" })
3094
+ createVariableLoader({ type: "ieee" }),
3095
+ createUnlocalizableLoader(options.isCacheRestore, options.returnUnlocalizedKeys)
3095
3096
  );
3096
3097
  case "yaml":
3097
3098
  return composeLoaders(
@@ -3320,6 +3321,152 @@ function _tryParseJSON(line) {
3320
3321
  }
3321
3322
  }
3322
3323
 
3324
+ // src/cli/processor/lingo.ts
3325
+ var __sdk = require('@lingo.dev/_sdk');
3326
+ function createLingoLocalizer(params) {
3327
+ return async (input2, onProgress) => {
3328
+ const lingo = new (0, __sdk.LingoDotDevEngine)({
3329
+ apiKey: params.apiKey,
3330
+ apiUrl: params.apiUrl
3331
+ });
3332
+ const result = await lingo.localizeObject(
3333
+ input2.processableData,
3334
+ {
3335
+ sourceLocale: input2.sourceLocale,
3336
+ targetLocale: input2.targetLocale,
3337
+ reference: {
3338
+ [input2.sourceLocale]: input2.sourceData,
3339
+ [input2.targetLocale]: input2.targetData
3340
+ }
3341
+ },
3342
+ onProgress
3343
+ );
3344
+ return result;
3345
+ };
3346
+ }
3347
+
3348
+ // src/cli/processor/openai.ts
3349
+ var _ai = require('ai');
3350
+ function createBasicTranslator(model, systemPrompt) {
3351
+ return async (input2, onProgress) => {
3352
+ if (!process.env.OPENAI_API_KEY) {
3353
+ throw new Error("OPENAI_API_KEY is not set");
3354
+ }
3355
+ const response = await _ai.generateText.call(void 0, {
3356
+ model,
3357
+ messages: [
3358
+ {
3359
+ role: "system",
3360
+ content: JSON.stringify({
3361
+ role: "system",
3362
+ content: systemPrompt.replaceAll("{source}", input2.sourceLocale).replaceAll("{target}", input2.targetLocale)
3363
+ })
3364
+ },
3365
+ {
3366
+ role: "user",
3367
+ content: JSON.stringify({
3368
+ sourceLocale: "en",
3369
+ targetLocale: "es",
3370
+ data: {
3371
+ message: "Hello, world!"
3372
+ }
3373
+ })
3374
+ },
3375
+ {
3376
+ role: "assistant",
3377
+ content: JSON.stringify({
3378
+ sourceLocale: "en",
3379
+ targetLocale: "es",
3380
+ data: {
3381
+ message: "Hola, mundo!"
3382
+ }
3383
+ })
3384
+ },
3385
+ {
3386
+ role: "user",
3387
+ content: JSON.stringify({
3388
+ sourceLocale: "en",
3389
+ targetLocale: "es",
3390
+ data: input2.processableData
3391
+ })
3392
+ }
3393
+ ]
3394
+ });
3395
+ const result = JSON.parse(response.text);
3396
+ return result;
3397
+ };
3398
+ }
3399
+
3400
+ // src/cli/processor/index.ts
3401
+ var _openai = require('@ai-sdk/openai');
3402
+ var _anthropic = require('@ai-sdk/anthropic');
3403
+ function createProcessor(provider, params) {
3404
+ if (!provider || provider.id === "lingo") {
3405
+ const result = createLingoLocalizer(params);
3406
+ return result;
3407
+ } else {
3408
+ const model = getPureModelProvider(provider);
3409
+ const result = createBasicTranslator(model, provider.prompt);
3410
+ return result;
3411
+ }
3412
+ }
3413
+ function getPureModelProvider(provider) {
3414
+ switch (_optionalChain([provider, 'optionalAccess', _137 => _137.id])) {
3415
+ case "openai":
3416
+ if (!process.env.OPENAI_API_KEY) {
3417
+ throw new Error("OPENAI_API_KEY is not set.");
3418
+ }
3419
+ return _openai.createOpenAI.call(void 0, {
3420
+ apiKey: process.env.OPENAI_API_KEY,
3421
+ baseURL: provider.baseUrl
3422
+ })(provider.model);
3423
+ case "anthropic":
3424
+ if (!process.env.ANTHROPIC_API_KEY) {
3425
+ throw new Error("ANTHROPIC_API_KEY is not set.");
3426
+ }
3427
+ return _anthropic.createAnthropic.call(void 0, {
3428
+ apiKey: process.env.ANTHROPIC_API_KEY
3429
+ })(provider.model);
3430
+ default:
3431
+ throw new Error(`Unsupported provider: ${_optionalChain([provider, 'optionalAccess', _138 => _138.id])}`);
3432
+ }
3433
+ }
3434
+
3435
+ // src/cli/utils/exp-backoff.ts
3436
+ function withExponentialBackoff(fn, maxAttempts = 3, baseDelay = 1e3) {
3437
+ return async (...args) => {
3438
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
3439
+ try {
3440
+ return await fn(...args);
3441
+ } catch (error) {
3442
+ if (attempt === maxAttempts - 1) throw error;
3443
+ const delay = baseDelay * Math.pow(2, attempt);
3444
+ await new Promise((resolve) => setTimeout(resolve, delay));
3445
+ }
3446
+ }
3447
+ throw new Error("Unreachable code");
3448
+ };
3449
+ }
3450
+
3451
+ // src/cli/utils/observability.ts
3452
+ var _posthognode = require('posthog-node');
3453
+ async function trackEvent(distinctId, event, properties) {
3454
+ if (process.env.DO_NOT_TRACK) {
3455
+ return;
3456
+ }
3457
+ const posthog = new (0, _posthognode.PostHog)("phc_eR0iSoQufBxNY36k0f0T15UvHJdTfHlh8rJcxsfhfXk", {
3458
+ host: "https://eu.i.posthog.com",
3459
+ flushAt: 1,
3460
+ flushInterval: 0
3461
+ });
3462
+ await posthog.capture({
3463
+ distinctId,
3464
+ event,
3465
+ properties
3466
+ });
3467
+ await posthog.shutdown();
3468
+ }
3469
+
3323
3470
  // src/cli/cmd/i18n.ts
3324
3471
  var i18n_default = new (0, _interactivecommander.Command)().command("i18n").description("Run Localization engine").helpOption("-h, --help", "Show help").option("--locale <locale>", "Locale to process", (val, prev) => prev ? [...prev, val] : [val]).option("--bucket <bucket>", "Bucket to process", (val, prev) => prev ? [...prev, val] : [val]).option(
3325
3472
  "--key <key>",
@@ -3344,6 +3491,7 @@ var i18n_default = new (0, _interactivecommander.Command)().command("i18n").desc
3344
3491
  ]);
3345
3492
  }
3346
3493
  let hasErrors = false;
3494
+ let authId = null;
3347
3495
  try {
3348
3496
  ora.start("Loading configuration...");
3349
3497
  const i18nConfig = getConfig();
@@ -3354,15 +3502,20 @@ var i18n_default = new (0, _interactivecommander.Command)().command("i18n").desc
3354
3502
  ora.succeed("Localization configuration is valid");
3355
3503
  ora.start("Connecting to Lingo.dev Localization Engine...");
3356
3504
  const auth = await validateAuth(settings);
3505
+ authId = auth.id;
3357
3506
  ora.succeed(`Authenticated as ${auth.email}`);
3507
+ trackEvent(authId, "cmd.i18n.start", {
3508
+ i18nConfig,
3509
+ flags
3510
+ });
3358
3511
  let buckets = getBuckets(i18nConfig);
3359
- if (_optionalChain([flags, 'access', _137 => _137.bucket, 'optionalAccess', _138 => _138.length])) {
3512
+ if (_optionalChain([flags, 'access', _139 => _139.bucket, 'optionalAccess', _140 => _140.length])) {
3360
3513
  buckets = buckets.filter((bucket) => flags.bucket.includes(bucket.type));
3361
3514
  }
3362
3515
  ora.succeed("Buckets retrieved");
3363
- if (_optionalChain([flags, 'access', _139 => _139.file, 'optionalAccess', _140 => _140.length])) {
3516
+ if (_optionalChain([flags, 'access', _141 => _141.file, 'optionalAccess', _142 => _142.length])) {
3364
3517
  buckets = buckets.map((bucket) => {
3365
- const paths = bucket.paths.filter((path15) => flags.file.find((file) => _optionalChain([path15, 'access', _141 => _141.pathPattern, 'optionalAccess', _142 => _142.match, 'call', _143 => _143(file)])));
3518
+ const paths = bucket.paths.filter((path15) => flags.file.find((file) => _optionalChain([path15, 'access', _143 => _143.pathPattern, 'optionalAccess', _144 => _144.match, 'call', _145 => _145(file)])));
3366
3519
  return { ...bucket, paths };
3367
3520
  }).filter((bucket) => bucket.paths.length > 0);
3368
3521
  if (buckets.length === 0) {
@@ -3378,7 +3531,7 @@ var i18n_default = new (0, _interactivecommander.Command)().command("i18n").desc
3378
3531
  });
3379
3532
  }
3380
3533
  }
3381
- const targetLocales = _optionalChain([flags, 'access', _144 => _144.locale, 'optionalAccess', _145 => _145.length]) ? flags.locale : i18nConfig.locale.targets;
3534
+ const targetLocales = _optionalChain([flags, 'access', _146 => _146.locale, 'optionalAccess', _147 => _147.length]) ? flags.locale : i18nConfig.locale.targets;
3382
3535
  const lockfileHelper = createLockfileHelper();
3383
3536
  ora.start("Ensuring i18n.lock exists...");
3384
3537
  if (!lockfileHelper.isLockfileExists()) {
@@ -3538,11 +3691,12 @@ var i18n_default = new (0, _interactivecommander.Command)().command("i18n").desc
3538
3691
  bucketOra.start(
3539
3692
  `[${sourceLocale} -> ${targetLocale}] [${Object.keys(processableData).length} entries] (0%) AI localization in progress...`
3540
3693
  );
3541
- const localizationEngine = createLocalizationEngineConnection({
3694
+ let processPayload = createProcessor(i18nConfig.provider, {
3542
3695
  apiKey: settings.auth.apiKey,
3543
3696
  apiUrl: settings.auth.apiUrl
3544
3697
  });
3545
- const processedTargetData = await localizationEngine.process(
3698
+ processPayload = withExponentialBackoff(processPayload, 3, 1e3);
3699
+ const processedTargetData = await processPayload(
3546
3700
  {
3547
3701
  sourceLocale,
3548
3702
  sourceData,
@@ -3616,11 +3770,19 @@ var i18n_default = new (0, _interactivecommander.Command)().command("i18n").desc
3616
3770
  if (flags.verbose) {
3617
3771
  ora.info("Cache file deleted.");
3618
3772
  }
3773
+ trackEvent(auth.id, "cmd.i18n.success", {
3774
+ i18nConfig,
3775
+ flags
3776
+ });
3619
3777
  } else {
3620
3778
  ora.warn("Localization completed with errors.");
3621
3779
  }
3622
3780
  } catch (error) {
3623
3781
  ora.fail(error.message);
3782
+ trackEvent(authId || "unknown", "cmd.i18n.error", {
3783
+ flags,
3784
+ error
3785
+ });
3624
3786
  process.exit(1);
3625
3787
  }
3626
3788
  });
@@ -3630,43 +3792,6 @@ function calculateDataDelta(args) {
3630
3792
  const result = _lodash2.default.chain(args.sourceData).pickBy((value, key) => newKeys.includes(key) || updatedKeys.includes(key)).value();
3631
3793
  return result;
3632
3794
  }
3633
- async function retryWithExponentialBackoff(operation, maxAttempts, baseDelay = 1e3) {
3634
- for (let attempt = 0; attempt < maxAttempts; attempt++) {
3635
- try {
3636
- return await operation();
3637
- } catch (error) {
3638
- if (attempt === maxAttempts - 1) throw error;
3639
- const delay = baseDelay * Math.pow(2, attempt);
3640
- await new Promise((resolve) => setTimeout(resolve, delay));
3641
- }
3642
- }
3643
- throw new Error("Unreachable code");
3644
- }
3645
- function createLocalizationEngineConnection(params) {
3646
- const engine = new (0, __sdk.LingoDotDevEngine)({
3647
- apiKey: params.apiKey,
3648
- apiUrl: params.apiUrl
3649
- });
3650
- return {
3651
- process: async (args, onProgress) => {
3652
- return retryWithExponentialBackoff(
3653
- () => engine.localizeObject(
3654
- args.processableData,
3655
- {
3656
- sourceLocale: args.sourceLocale,
3657
- targetLocale: args.targetLocale,
3658
- reference: {
3659
- [args.sourceLocale]: args.sourceData,
3660
- [args.targetLocale]: args.targetData
3661
- }
3662
- },
3663
- onProgress
3664
- ),
3665
- _nullishCoalesce(params.maxRetries, () => ( 3))
3666
- );
3667
- }
3668
- };
3669
- }
3670
3795
  function parseFlags(options) {
3671
3796
  return _zod2.default.object({
3672
3797
  apiKey: _zod2.default.string().optional(),
@@ -3713,12 +3838,12 @@ function validateParams(i18nConfig, flags) {
3713
3838
  message: "No buckets found in i18n.json. Please add at least one bucket containing i18n content.",
3714
3839
  docUrl: "bucketNotFound"
3715
3840
  });
3716
- } else if (_optionalChain([flags, 'access', _146 => _146.locale, 'optionalAccess', _147 => _147.some, 'call', _148 => _148((locale) => !i18nConfig.locale.targets.includes(locale))])) {
3841
+ } else if (_optionalChain([flags, 'access', _148 => _148.locale, 'optionalAccess', _149 => _149.some, 'call', _150 => _150((locale) => !i18nConfig.locale.targets.includes(locale))])) {
3717
3842
  throw new CLIError({
3718
3843
  message: `One or more specified locales do not exist in i18n.json locale.targets. Please add them to the list and try again.`,
3719
3844
  docUrl: "localeTargetNotFound"
3720
3845
  });
3721
- } else if (_optionalChain([flags, 'access', _149 => _149.bucket, 'optionalAccess', _150 => _150.some, 'call', _151 => _151((bucket) => !i18nConfig.buckets[bucket])])) {
3846
+ } else if (_optionalChain([flags, 'access', _151 => _151.bucket, 'optionalAccess', _152 => _152.some, 'call', _153 => _153((bucket) => !i18nConfig.buckets[bucket])])) {
3722
3847
  throw new CLIError({
3723
3848
  message: `One or more specified buckets do not exist in i18n.json. Please add them to the list and try again.`,
3724
3849
  docUrl: "bucketNotFound"
@@ -4080,7 +4205,7 @@ var InBranchFlow = class extends IntegrationFlow {
4080
4205
  _child_process.execSync.call(void 0, `git config --global safe.directory ${process.cwd()}`);
4081
4206
  _child_process.execSync.call(void 0, `git config user.name "${gitConfig.userName}"`);
4082
4207
  _child_process.execSync.call(void 0, `git config user.email "${gitConfig.userEmail}"`);
4083
- _optionalChain([this, 'access', _152 => _152.platformKit, 'optionalAccess', _153 => _153.gitConfig, 'call', _154 => _154()]);
4208
+ _optionalChain([this, 'access', _154 => _154.platformKit, 'optionalAccess', _155 => _155.gitConfig, 'call', _156 => _156()]);
4084
4209
  _child_process.execSync.call(void 0, `git fetch origin ${baseBranchName}`, { stdio: "inherit" });
4085
4210
  _child_process.execSync.call(void 0, `git checkout ${baseBranchName} --`, { stdio: "inherit" });
4086
4211
  if (!processOwnCommits) {
@@ -4105,7 +4230,7 @@ var InBranchFlow = class extends IntegrationFlow {
4105
4230
  // ../../action/src/flows/pull-request.ts
4106
4231
  var PullRequestFlow = class extends InBranchFlow {
4107
4232
  async preRun() {
4108
- const canContinue = await _optionalChain([super.preRun.bind(this), 'optionalCall', _155 => _155()]);
4233
+ const canContinue = await _optionalChain([super.preRun.bind(this), 'optionalCall', _157 => _157()]);
4109
4234
  if (!canContinue) {
4110
4235
  return false;
4111
4236
  }
@@ -4317,10 +4442,10 @@ var BitbucketPlatformKit = class extends PlatformKit {
4317
4442
  repo_slug: this.platformConfig.repositoryName,
4318
4443
  state: "OPEN"
4319
4444
  }).then(({ data: { values } }) => {
4320
- return _optionalChain([values, 'optionalAccess', _156 => _156.find, 'call', _157 => _157(
4321
- ({ source, destination }) => _optionalChain([source, 'optionalAccess', _158 => _158.branch, 'optionalAccess', _159 => _159.name]) === branch && _optionalChain([destination, 'optionalAccess', _160 => _160.branch, 'optionalAccess', _161 => _161.name]) === this.platformConfig.baseBranchName
4445
+ return _optionalChain([values, 'optionalAccess', _158 => _158.find, 'call', _159 => _159(
4446
+ ({ source, destination }) => _optionalChain([source, 'optionalAccess', _160 => _160.branch, 'optionalAccess', _161 => _161.name]) === branch && _optionalChain([destination, 'optionalAccess', _162 => _162.branch, 'optionalAccess', _163 => _163.name]) === this.platformConfig.baseBranchName
4322
4447
  )]);
4323
- }).then((pr) => _optionalChain([pr, 'optionalAccess', _162 => _162.id]));
4448
+ }).then((pr) => _optionalChain([pr, 'optionalAccess', _164 => _164.id]));
4324
4449
  }
4325
4450
  async closePullRequest({ pullRequestNumber }) {
4326
4451
  await this.bb.repositories.declinePullRequest({
@@ -4407,7 +4532,7 @@ var GitHubPlatformKit = class extends PlatformKit {
4407
4532
  repo: this.platformConfig.repositoryName,
4408
4533
  base: this.platformConfig.baseBranchName,
4409
4534
  state: "open"
4410
- }).then(({ data }) => data[0]).then((pr) => _optionalChain([pr, 'optionalAccess', _163 => _163.number]));
4535
+ }).then(({ data }) => data[0]).then((pr) => _optionalChain([pr, 'optionalAccess', _165 => _165.number]));
4411
4536
  }
4412
4537
  async closePullRequest({ pullRequestNumber }) {
4413
4538
  await this.octokit.rest.pulls.update({
@@ -4523,7 +4648,7 @@ var GitlabPlatformKit = class extends PlatformKit {
4523
4648
  sourceBranch: branch,
4524
4649
  state: "opened"
4525
4650
  });
4526
- return _optionalChain([mergeRequests, 'access', _164 => _164[0], 'optionalAccess', _165 => _165.iid]);
4651
+ return _optionalChain([mergeRequests, 'access', _166 => _166[0], 'optionalAccess', _167 => _167.iid]);
4527
4652
  }
4528
4653
  async closePullRequest({ pullRequestNumber }) {
4529
4654
  await this.gitlab.MergeRequests.edit(this.platformConfig.gitlabProjectId, pullRequestNumber, {
@@ -4577,7 +4702,7 @@ async function main() {
4577
4702
  const { isPullRequestMode } = platformKit.config;
4578
4703
  ora.info(`Pull request mode: ${isPullRequestMode ? "on" : "off"}`);
4579
4704
  const flow = isPullRequestMode ? new PullRequestFlow(ora, platformKit) : new InBranchFlow(ora, platformKit);
4580
- const canRun = await _optionalChain([flow, 'access', _166 => _166.preRun, 'optionalCall', _167 => _167()]);
4705
+ const canRun = await _optionalChain([flow, 'access', _168 => _168.preRun, 'optionalCall', _169 => _169()]);
4581
4706
  if (canRun === false) {
4582
4707
  return;
4583
4708
  }
@@ -4585,7 +4710,7 @@ async function main() {
4585
4710
  if (!hasChanges) {
4586
4711
  return;
4587
4712
  }
4588
- await _optionalChain([flow, 'access', _168 => _168.postRun, 'optionalCall', _169 => _169()]);
4713
+ await _optionalChain([flow, 'access', _170 => _170.postRun, 'optionalCall', _171 => _171()]);
4589
4714
  }
4590
4715
 
4591
4716
  // src/cli/cmd/ci.ts
@@ -4607,7 +4732,7 @@ var ci_default = new (0, _interactivecommander.Command)().command("ci").descript
4607
4732
  }
4608
4733
  const env = {
4609
4734
  LINGODOTDEV_API_KEY: settings.auth.apiKey,
4610
- LINGODOTDEV_PULL_REQUEST: _optionalChain([options, 'access', _170 => _170.pullRequest, 'optionalAccess', _171 => _171.toString, 'call', _172 => _172()]) || "false",
4735
+ LINGODOTDEV_PULL_REQUEST: _optionalChain([options, 'access', _172 => _172.pullRequest, 'optionalAccess', _173 => _173.toString, 'call', _174 => _174()]) || "false",
4611
4736
  ...options.commitMessage && { LINGODOTDEV_COMMIT_MESSAGE: options.commitMessage },
4612
4737
  ...options.pullRequestTitle && { LINGODOTDEV_PULL_REQUEST_TITLE: options.pullRequestTitle },
4613
4738
  ...options.workingDirectory && { LINGODOTDEV_WORKING_DIRECTORY: options.workingDirectory },
@@ -4620,7 +4745,7 @@ var ci_default = new (0, _interactivecommander.Command)().command("ci").descript
4620
4745
  // package.json
4621
4746
  var package_default = {
4622
4747
  name: "lingo.dev",
4623
- version: "0.79.5",
4748
+ version: "0.80.0",
4624
4749
  description: "Lingo.dev CLI",
4625
4750
  private: false,
4626
4751
  publishConfig: {
@@ -4677,6 +4802,8 @@ var package_default = {
4677
4802
  author: "",
4678
4803
  license: "Apache-2.0",
4679
4804
  dependencies: {
4805
+ "@ai-sdk/anthropic": "^1.2.6",
4806
+ "@ai-sdk/openai": "^1.3.7",
4680
4807
  "@datocms/cma-client-node": "^3.4.0",
4681
4808
  "@gitbeaker/rest": "^39.34.3",
4682
4809
  "@inquirer/prompts": "^7.2.3",
@@ -4684,6 +4811,7 @@ var package_default = {
4684
4811
  "@lingo.dev/_spec": "workspace:*",
4685
4812
  "@modelcontextprotocol/sdk": "^1.5.0",
4686
4813
  "@paralleldrive/cuid2": "^2.2.2",
4814
+ ai: "^4.3.2",
4687
4815
  bitbucket: "^2.12.0",
4688
4816
  chalk: "^5.4.1",
4689
4817
  cors: "^2.8.5",
@@ -4720,6 +4848,7 @@ var package_default = {
4720
4848
  "p-limit": "^6.2.0",
4721
4849
  "php-array-reader": "^2.1.2",
4722
4850
  plist: "^3.1.0",
4851
+ "posthog-node": "^4.11.2",
4723
4852
  prettier: "^3.4.2",
4724
4853
  "properties-parser": "^0.6.0",
4725
4854
  slugify: "^1.6.6",