@intlayer/cli 7.5.9 → 7.5.11

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 (62) hide show
  1. package/README.md +9 -2
  2. package/dist/cjs/ci.cjs +73 -0
  3. package/dist/cjs/ci.cjs.map +1 -0
  4. package/dist/cjs/cli.cjs +37 -2
  5. package/dist/cjs/cli.cjs.map +1 -1
  6. package/dist/cjs/editor.cjs +1 -1
  7. package/dist/cjs/listContentDeclaration.cjs +6 -2
  8. package/dist/cjs/listContentDeclaration.cjs.map +1 -1
  9. package/dist/cjs/listProjects.cjs +28 -0
  10. package/dist/cjs/listProjects.cjs.map +1 -0
  11. package/dist/cjs/pushConfig.cjs +1 -1
  12. package/dist/cjs/pushConfig.cjs.map +1 -1
  13. package/dist/cjs/translateDoc.cjs +41 -10
  14. package/dist/cjs/translateDoc.cjs.map +1 -1
  15. package/dist/cjs/utils/checkAccess.cjs +32 -6
  16. package/dist/cjs/utils/checkAccess.cjs.map +1 -1
  17. package/dist/cjs/utils/checkConfigConsistency.cjs +24 -0
  18. package/dist/cjs/utils/checkConfigConsistency.cjs.map +1 -0
  19. package/dist/cjs/utils/setupAI.cjs +20 -11
  20. package/dist/cjs/utils/setupAI.cjs.map +1 -1
  21. package/dist/esm/auth/login.mjs +16 -16
  22. package/dist/esm/auth/login.mjs.map +1 -1
  23. package/dist/esm/ci.mjs +72 -0
  24. package/dist/esm/ci.mjs.map +1 -0
  25. package/dist/esm/cli.mjs +37 -2
  26. package/dist/esm/cli.mjs.map +1 -1
  27. package/dist/esm/editor.mjs +1 -1
  28. package/dist/esm/listContentDeclaration.mjs +6 -2
  29. package/dist/esm/listContentDeclaration.mjs.map +1 -1
  30. package/dist/esm/listProjects.mjs +27 -0
  31. package/dist/esm/listProjects.mjs.map +1 -0
  32. package/dist/esm/pull.mjs +6 -6
  33. package/dist/esm/pull.mjs.map +1 -1
  34. package/dist/esm/push/push.mjs +7 -7
  35. package/dist/esm/push/push.mjs.map +1 -1
  36. package/dist/esm/pushConfig.mjs +1 -1
  37. package/dist/esm/pushConfig.mjs.map +1 -1
  38. package/dist/esm/translateDoc.mjs +41 -10
  39. package/dist/esm/translateDoc.mjs.map +1 -1
  40. package/dist/esm/utils/checkAccess.mjs +32 -6
  41. package/dist/esm/utils/checkAccess.mjs.map +1 -1
  42. package/dist/esm/utils/checkConfigConsistency.mjs +23 -0
  43. package/dist/esm/utils/checkConfigConsistency.mjs.map +1 -0
  44. package/dist/esm/utils/setupAI.mjs +20 -11
  45. package/dist/esm/utils/setupAI.mjs.map +1 -1
  46. package/dist/types/ci.d.ts +5 -0
  47. package/dist/types/ci.d.ts.map +1 -0
  48. package/dist/types/cli.d.ts.map +1 -1
  49. package/dist/types/config.d.ts.map +1 -1
  50. package/dist/types/listContentDeclaration.d.ts +2 -0
  51. package/dist/types/listContentDeclaration.d.ts.map +1 -1
  52. package/dist/types/listProjects.d.ts +11 -0
  53. package/dist/types/listProjects.d.ts.map +1 -0
  54. package/dist/types/pull.d.ts.map +1 -1
  55. package/dist/types/translateDoc.d.ts +10 -1
  56. package/dist/types/translateDoc.d.ts.map +1 -1
  57. package/dist/types/utils/checkAccess.d.ts +2 -2
  58. package/dist/types/utils/checkAccess.d.ts.map +1 -1
  59. package/dist/types/utils/checkConfigConsistency.d.ts +13 -0
  60. package/dist/types/utils/checkConfigConsistency.d.ts.map +1 -0
  61. package/dist/types/utils/setupAI.d.ts.map +1 -1
  62. package/package.json +11 -10
@@ -15,22 +15,26 @@ import fg from "fast-glob";
15
15
  //#region src/translateDoc.ts
16
16
  /**
17
17
  * Translate a single file for a given locale
18
+ * Returns TRUE if successful, FALSE if failed/skipped
18
19
  */
19
- const translateFile = async (baseFilePath, outputFilePath, locale, baseLocale, configuration, aiOptions, customInstructions, aiClient, aiConfig) => {
20
+ const translateFile = async (baseFilePath, outputFilePath, locale, baseLocale, configuration, errorState, aiOptions, customInstructions, aiClient, aiConfig) => {
21
+ if (errorState.shouldStop) return false;
22
+ const appLogger = getAppLogger(configuration, { config: { prefix: "" } });
20
23
  try {
21
- const appLogger = getAppLogger(configuration, { config: { prefix: "" } });
22
24
  const fileContent = await readFile(baseFilePath, "utf-8");
23
25
  let fileResultContent = fileContent;
24
- const basePrompt = readAsset("./prompts/TRANSLATE_PROMPT.md", "utf-8").replaceAll("{{localeName}}", `${formatLocale(locale, false)}`).replaceAll("{{baseLocaleName}}", `${formatLocale(baseLocale, false)}`).replace("{{applicationContext}}", aiOptions?.applicationContext ?? "-").replace("{{customInstructions}}", customInstructions ?? "-");
25
26
  const filePrefix = [colon(`${ANSIColors.GREY_DARK}[${formatPath(baseFilePath)}${ANSIColors.GREY_DARK}] `, { colSize: 40 }), `→ ${ANSIColors.RESET}`].join("");
26
27
  const prefix = [colon(`${ANSIColors.GREY_DARK}[${formatPath(baseFilePath)}${ANSIColors.GREY_DARK}][${formatLocale(locale)}${ANSIColors.GREY_DARK}] `, { colSize: 40 }), `→ ${ANSIColors.RESET}`].join("");
27
28
  const chunks = chunkText(fileContent);
28
29
  appLogger(`${filePrefix}Base file splitted into ${colorizeNumber(chunks.length)} chunks`);
30
+ const translatedParts = [];
31
+ const basePrompt = readAsset("./prompts/TRANSLATE_PROMPT.md", "utf-8").replaceAll("{{localeName}}", `${formatLocale(locale, false)}`).replaceAll("{{baseLocaleName}}", `${formatLocale(baseLocale, false)}`).replace("{{applicationContext}}", aiOptions?.applicationContext ?? "-").replace("{{customInstructions}}", customInstructions ?? "-");
29
32
  for await (const [i, chunk] of chunks.entries()) {
33
+ if (errorState.shouldStop) return false;
30
34
  const isFirstChunk = i === 0;
31
- const getPrevChunkPrompt = () => `**CHUNK ${i} of ${chunks.length}** that has been translated in ${formatLocale(locale)}:\n///chunkStart///` + getChunk(fileResultContent, chunks[i - 1]) + `///chunkEnd///`;
32
- const getBaseChunkContextPrompt = () => `**CHUNK ${i + 1} to ${Math.min(i + 3, chunks.length)} of ${chunks.length}** is the base chunk in ${formatLocale(baseLocale, false)} as reference.\n///chunksStart///` + (chunks[i - 1]?.content ?? "") + chunks[i].content + (chunks[i + 1]?.content ?? "") + `///chunksEnd///`;
33
35
  const fileToTranslateCurrentChunk = chunk.content;
36
+ const getPrevChunkPrompt = () => `**CHUNK ${i} of ${chunks.length}** that has been translated in ${formatLocale(locale)}:\n///chunkStart///` + getChunk(translatedParts.join(""), chunks[i - 1]) + `///chunkEnd///`;
37
+ const getBaseChunkContextPrompt = () => `**CHUNK ${i + 1} to ${Math.min(i + 3, chunks.length)} of ${chunks.length}** is the base chunk in ${formatLocale(baseLocale, false)} as reference.\n///chunksStart///` + (chunks[i - 1]?.content ?? "") + chunks[i].content + (chunks[i + 1]?.content ?? "") + `///chunksEnd///`;
34
38
  const chunkTranslation = await retryManager(async () => {
35
39
  const result = await chunkInference([
36
40
  {
@@ -47,7 +51,12 @@ const translateFile = async (baseFilePath, outputFilePath, locale, baseLocale, c
47
51
  }],
48
52
  {
49
53
  role: "system",
50
- content: `The next user message will be the **CHUNK ${colorizeNumber(i + 1)} of ${colorizeNumber(chunks.length)}** in ${formatLocale(baseLocale, false)} to translate in ${formatLocale(locale, false)}:`
54
+ content: `The next user message will be the **CHUNK ${colorizeNumber(i + 1)} of ${colorizeNumber(chunks.length)}** in ${formatLocale(baseLocale, false)} to translate in ${formatLocale(locale, false)}.\n
55
+ STRICT INSTRUCTIONS:
56
+ 1. Translate ONLY the content of this specific chunk.
57
+ 2. Do NOT repeat the content from the previous chunk.
58
+ 3. Start the translation exactly where the previous chunk ended.
59
+ 4. Preserve all code blocks and formatting exactly.`
51
60
  },
52
61
  {
53
62
  role: "user",
@@ -67,12 +76,25 @@ const translateFile = async (baseFilePath, outputFilePath, locale, baseLocale, c
67
76
  })();
68
77
  fileResultContent = fileResultContent.replace(fileToTranslateCurrentChunk, chunkTranslation);
69
78
  }
79
+ const finalContent = translatedParts.join("");
70
80
  mkdirSync(dirname(outputFilePath), { recursive: true });
71
- writeFileSync(outputFilePath, fileResultContent);
81
+ writeFileSync(outputFilePath, finalContent);
72
82
  const relativePath = relative(configuration.content.baseDir, outputFilePath);
73
83
  appLogger(`${colorize("✔", ANSIColors.GREEN)} File ${formatPath(relativePath)} created/updated successfully.`);
84
+ return true;
74
85
  } catch (error) {
75
- console.error(error);
86
+ errorState.count++;
87
+ const errorString = JSON.stringify(error);
88
+ const errorMessage = error?.message ?? "";
89
+ if (errorString.includes("AI_ACCESS_DENIED") || errorMessage.includes("Access keys") || errorMessage.includes("Access denied") || errorMessage.includes("Invalid Access keys")) {
90
+ errorState.count = errorState.maxErrors + 1;
91
+ appLogger(`${colorize("✖", ANSIColors.RED)} Critical Authentication Error. Aborting all tasks.`);
92
+ }
93
+ if (errorState.count >= errorState.maxErrors && !errorState.shouldStop) {
94
+ errorState.shouldStop = true;
95
+ appLogger(`${colorize("✖", ANSIColors.RED)} Too many errors (${errorState.count}). Stopping process.`);
96
+ }
97
+ return false;
76
98
  }
77
99
  };
78
100
  /**
@@ -97,8 +119,16 @@ const translateDoc = async ({ docPattern, locales, excludedGlobPattern, baseLoca
97
119
  appLogger(`Base locale is ${formatLocale(baseLocale)}`);
98
120
  appLogger(`Translating ${colorizeNumber(locales.length)} locales: [ ${formatLocale(locales)} ]`);
99
121
  appLogger(`Translating ${colorizeNumber(docList.length)} files:`);
100
- appLogger(docList.map((path) => ` - ${formatPath(path)}\n`));
122
+ docList.forEach((path) => {
123
+ appLogger(` - ${formatPath(path)}`);
124
+ });
125
+ const errorState = {
126
+ count: 0,
127
+ maxErrors: 5,
128
+ shouldStop: false
129
+ };
101
130
  await parallelize(docList.flatMap((docPath) => locales.map((locale) => async () => {
131
+ if (errorState.shouldStop) return;
102
132
  appLogger(`Translating file: ${formatPath(docPath)} to ${formatLocale(locale)}`);
103
133
  const absoluteBaseFilePath = join(configuration.content.baseDir, docPath);
104
134
  const outputFilePath = getOutputFilePath(absoluteBaseFilePath, locale, baseLocale);
@@ -120,8 +150,9 @@ const translateDoc = async ({ docPattern, locales, excludedGlobPattern, baseLoca
120
150
  appLogger(fileModificationData.message);
121
151
  return;
122
152
  }
123
- await translateFile(absoluteBaseFilePath, outputFilePath, locale, baseLocale, configuration, aiOptions, customInstructions, aiClient, aiConfig);
153
+ await translateFile(absoluteBaseFilePath, outputFilePath, locale, baseLocale, configuration, errorState, aiOptions, customInstructions, aiClient, aiConfig);
124
154
  })), (task) => task(), nbSimultaneousFileProcessed ?? 3);
155
+ if (errorState.count > 0) appLogger(`Process finished with ${colorizeNumber(errorState.count)} error${errorState.count === 1 ? "" : "s"}.`);
125
156
  };
126
157
 
127
158
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"translateDoc.mjs","names":["docList: string[]"],"sources":["../../src/translateDoc.ts"],"sourcesContent":["import { existsSync, mkdirSync, writeFileSync } from 'node:fs';\nimport { readFile } from 'node:fs/promises';\nimport { dirname, join, relative } from 'node:path';\nimport { readAsset } from 'utils:asset';\nimport type { AIConfig, AIOptions } from '@intlayer/ai';\nimport {\n formatLocale,\n formatPath,\n getChunk,\n type ListGitFilesOptions,\n listGitFiles,\n parallelize,\n} from '@intlayer/chokidar';\nimport {\n ANSIColors,\n colon,\n colorize,\n colorizeNumber,\n type GetConfigurationOptions,\n getAppLogger,\n getConfiguration,\n retryManager,\n} from '@intlayer/config';\nimport type { IntlayerConfig, Locale } from '@intlayer/types';\nimport fg from 'fast-glob';\nimport { chunkText } from './utils/calculateChunks';\nimport { checkFileModifiedRange } from './utils/checkFileModifiedRange';\nimport { chunkInference } from './utils/chunkInference';\nimport { fixChunkStartEndChars } from './utils/fixChunkStartEndChars';\nimport { getOutputFilePath } from './utils/getOutputFilePath';\nimport { type AIClient, setupAI } from './utils/setupAI';\n\n/**\n * Translate a single file for a given locale\n */\nexport const translateFile = async (\n baseFilePath: string,\n outputFilePath: string,\n locale: Locale,\n baseLocale: Locale,\n configuration: IntlayerConfig,\n aiOptions?: AIOptions,\n customInstructions?: string,\n aiClient?: AIClient,\n aiConfig?: AIConfig\n) => {\n try {\n const appLogger = getAppLogger(configuration, {\n config: {\n prefix: '',\n },\n });\n\n // Determine the target locale file path\n const fileContent = await readFile(baseFilePath, 'utf-8');\n\n let fileResultContent = fileContent;\n\n // Prepare the base prompt for ChatGPT\n const basePrompt = readAsset('./prompts/TRANSLATE_PROMPT.md', 'utf-8')\n .replaceAll('{{localeName}}', `${formatLocale(locale, false)}`)\n .replaceAll('{{baseLocaleName}}', `${formatLocale(baseLocale, false)}`)\n .replace('{{applicationContext}}', aiOptions?.applicationContext ?? '-')\n .replace('{{customInstructions}}', customInstructions ?? '-');\n\n const filePrefixText = `${ANSIColors.GREY_DARK}[${formatPath(baseFilePath)}${ANSIColors.GREY_DARK}] `;\n const filePrefix = [\n colon(filePrefixText, { colSize: 40 }),\n `→ ${ANSIColors.RESET}`,\n ].join('');\n\n const prefixText = `${ANSIColors.GREY_DARK}[${formatPath(baseFilePath)}${ANSIColors.GREY_DARK}][${formatLocale(locale)}${ANSIColors.GREY_DARK}] `;\n const prefix = [\n colon(prefixText, { colSize: 40 }),\n `→ ${ANSIColors.RESET}`,\n ].join('');\n\n // 1. Chunk the file by number of lines instead of characters\n const chunks = chunkText(fileContent);\n appLogger(\n `${filePrefix}Base file splitted into ${colorizeNumber(chunks.length)} chunks`\n );\n\n for await (const [i, chunk] of chunks.entries()) {\n const isFirstChunk = i === 0;\n\n // Build the chunk-specific prompt\n const getPrevChunkPrompt = () =>\n `**CHUNK ${i} of ${chunks.length}** that has been translated in ${formatLocale(locale)}:\\n` +\n `///chunkStart///` +\n getChunk(fileResultContent, chunks[i - 1]) +\n `///chunkEnd///`;\n\n const getBaseChunkContextPrompt = () =>\n `**CHUNK ${i + 1} to ${Math.min(i + 3, chunks.length)} of ${chunks.length}** is the base chunk in ${formatLocale(baseLocale, false)} as reference.\\n` +\n `///chunksStart///` +\n (chunks[i - 1]?.content ?? '') +\n chunks[i].content +\n (chunks[i + 1]?.content ?? '') +\n `///chunksEnd///`;\n\n const fileToTranslateCurrentChunk = chunk.content;\n\n // Make the actual translation call\n const chunkTranslation = await retryManager(async () => {\n const result = await chunkInference(\n [\n { role: 'system', content: basePrompt },\n\n { role: 'system', content: getBaseChunkContextPrompt() },\n ...(isFirstChunk\n ? []\n : [{ role: 'system', content: getPrevChunkPrompt() } as const]),\n {\n role: 'system',\n content: `The next user message will be the **CHUNK ${colorizeNumber(i + 1)} of ${colorizeNumber(chunks.length)}** in ${formatLocale(baseLocale, false)} to translate in ${formatLocale(locale, false)}:`,\n },\n { role: 'user', content: fileToTranslateCurrentChunk },\n ],\n aiOptions,\n configuration,\n aiClient,\n aiConfig\n );\n\n appLogger(\n [\n `${prefix}`,\n `${ANSIColors.GREY_DARK}[Chunk `,\n colorizeNumber(i + 1),\n `${ANSIColors.GREY_DARK} of `,\n colorizeNumber(chunks.length),\n `${ANSIColors.GREY_DARK}] →${ANSIColors.RESET} `,\n `${colorizeNumber(result.tokenUsed)} tokens used`,\n ].join('')\n );\n\n const fixedTranslatedChunkResult = fixChunkStartEndChars(\n result?.fileContent,\n fileToTranslateCurrentChunk\n );\n\n return fixedTranslatedChunkResult;\n })();\n\n // Replace the chunk in the file content\n fileResultContent = fileResultContent.replace(\n fileToTranslateCurrentChunk,\n chunkTranslation\n );\n }\n\n // 4. Write the final translation to the appropriate file path\n mkdirSync(dirname(outputFilePath), { recursive: true });\n writeFileSync(outputFilePath, fileResultContent);\n\n const relativePath = relative(\n configuration.content.baseDir,\n outputFilePath\n );\n\n appLogger(\n `${colorize('✔', ANSIColors.GREEN)} File ${formatPath(relativePath)} created/updated successfully.`\n );\n } catch (error) {\n console.error(error);\n }\n};\n\ntype TranslateDocOptions = {\n docPattern: string[];\n locales: Locale[];\n excludedGlobPattern: string[];\n baseLocale: Locale;\n aiOptions?: AIOptions;\n nbSimultaneousFileProcessed?: number;\n configOptions?: GetConfigurationOptions;\n customInstructions?: string;\n skipIfModifiedBefore?: number | string | Date;\n skipIfModifiedAfter?: number | string | Date;\n skipIfExists?: boolean;\n gitOptions?: ListGitFilesOptions;\n};\n\n/**\n * Main translate function: scans all .md files in \"en/\" (unless you specified DOC_LIST),\n * then translates them to each locale in LOCALE_LIST.\n */\nexport const translateDoc = async ({\n docPattern,\n locales,\n excludedGlobPattern,\n baseLocale,\n aiOptions,\n nbSimultaneousFileProcessed,\n configOptions,\n customInstructions,\n skipIfModifiedBefore,\n skipIfModifiedAfter,\n skipIfExists,\n gitOptions,\n}: TranslateDocOptions) => {\n const configuration = getConfiguration(configOptions);\n const appLogger = getAppLogger(configuration);\n\n if (nbSimultaneousFileProcessed && nbSimultaneousFileProcessed > 10) {\n appLogger(\n `Warning: nbSimultaneousFileProcessed is set to ${nbSimultaneousFileProcessed}, which is greater than 10. Setting it to 10.`\n );\n nbSimultaneousFileProcessed = 10; // Limit the number of simultaneous file processed to 10\n }\n\n let docList: string[] = await fg(docPattern, {\n ignore: excludedGlobPattern,\n });\n\n const aiResult = await setupAI(configuration, aiOptions);\n\n if (!aiResult?.hasAIAccess) return;\n\n const { aiClient, aiConfig } = aiResult;\n\n if (gitOptions) {\n const gitChangedFiles = await listGitFiles(gitOptions);\n\n if (gitChangedFiles) {\n // Convert dictionary file paths to be relative to git root for comparison\n\n // Filter dictionaries based on git changed files\n docList = docList.filter((path) =>\n gitChangedFiles.some((gitFile) => join(process.cwd(), path) === gitFile)\n );\n }\n }\n\n // OAuth handled by API proxy internally\n\n appLogger(`Base locale is ${formatLocale(baseLocale)}`);\n appLogger(\n `Translating ${colorizeNumber(locales.length)} locales: [ ${formatLocale(locales)} ]`\n );\n\n appLogger(`Translating ${colorizeNumber(docList.length)} files:`);\n appLogger(docList.map((path) => ` - ${formatPath(path)}\\n`));\n\n // Create all tasks to be processed\n const allTasks = docList.flatMap((docPath) =>\n locales.map((locale) => async () => {\n appLogger(\n `Translating file: ${formatPath(docPath)} to ${formatLocale(locale)}`\n );\n\n const absoluteBaseFilePath = join(configuration.content.baseDir, docPath);\n const outputFilePath = getOutputFilePath(\n absoluteBaseFilePath,\n locale,\n baseLocale\n );\n\n // Skip if file exists and skipIfExists option is enabled\n if (skipIfExists && existsSync(outputFilePath)) {\n const relativePath = relative(\n configuration.content.baseDir,\n outputFilePath\n );\n appLogger(\n `${colorize('⊘', ANSIColors.YELLOW)} File ${formatPath(relativePath)} already exists, skipping.`\n );\n return;\n }\n\n // check if the file exist, otherwise create it\n if (!existsSync(outputFilePath)) {\n const relativePath = relative(\n configuration.content.baseDir,\n outputFilePath\n );\n appLogger(\n `File ${formatPath(relativePath)} does not exist, creating it...`\n );\n mkdirSync(dirname(outputFilePath), { recursive: true });\n writeFileSync(outputFilePath, '');\n }\n\n const fileModificationData = checkFileModifiedRange(outputFilePath, {\n skipIfModifiedBefore,\n skipIfModifiedAfter,\n });\n\n if (fileModificationData.isSkipped) {\n appLogger(fileModificationData.message);\n return;\n }\n\n await translateFile(\n absoluteBaseFilePath,\n outputFilePath,\n locale as Locale,\n baseLocale,\n configuration,\n aiOptions,\n customInstructions,\n aiClient,\n aiConfig\n );\n })\n );\n\n await parallelize(\n allTasks,\n (task) => task(),\n nbSimultaneousFileProcessed ?? 3\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"translateDoc.mjs","names":["translatedParts: string[]","error: any","docList: string[]","errorState: ErrorState"],"sources":["../../src/translateDoc.ts"],"sourcesContent":["import { existsSync, mkdirSync, writeFileSync } from 'node:fs';\nimport { readFile } from 'node:fs/promises';\nimport { dirname, join, relative } from 'node:path';\nimport { readAsset } from 'utils:asset';\nimport type { AIConfig, AIOptions } from '@intlayer/ai';\nimport {\n formatLocale,\n formatPath,\n getChunk,\n type ListGitFilesOptions,\n listGitFiles,\n parallelize,\n} from '@intlayer/chokidar';\nimport {\n ANSIColors,\n colon,\n colorize,\n colorizeNumber,\n type GetConfigurationOptions,\n getAppLogger,\n getConfiguration,\n retryManager,\n} from '@intlayer/config';\nimport type { IntlayerConfig, Locale } from '@intlayer/types';\nimport fg from 'fast-glob';\nimport { chunkText } from './utils/calculateChunks';\nimport { checkFileModifiedRange } from './utils/checkFileModifiedRange';\nimport { chunkInference } from './utils/chunkInference';\nimport { fixChunkStartEndChars } from './utils/fixChunkStartEndChars';\nimport { getOutputFilePath } from './utils/getOutputFilePath';\nimport { type AIClient, setupAI } from './utils/setupAI';\n\n/**\n * Shared error state for circuit breaker pattern\n */\ntype ErrorState = {\n count: number;\n maxErrors: number;\n shouldStop: boolean;\n};\n\n/**\n * Translate a single file for a given locale\n * Returns TRUE if successful, FALSE if failed/skipped\n */\nexport const translateFile = async (\n baseFilePath: string,\n outputFilePath: string,\n locale: Locale,\n baseLocale: Locale,\n configuration: IntlayerConfig,\n errorState: ErrorState,\n aiOptions?: AIOptions,\n customInstructions?: string,\n aiClient?: AIClient,\n aiConfig?: AIConfig\n): Promise<boolean> => {\n // Circuit Breaker Check\n if (errorState.shouldStop) {\n return false;\n }\n\n const appLogger = getAppLogger(configuration, {\n config: {\n prefix: '',\n },\n });\n\n try {\n // Read File\n const fileContent = await readFile(baseFilePath, 'utf-8');\n\n let fileResultContent = fileContent;\n\n // Prepare formatting\n const filePrefixText = `${ANSIColors.GREY_DARK}[${formatPath(baseFilePath)}${ANSIColors.GREY_DARK}] `;\n const filePrefix = [\n colon(filePrefixText, { colSize: 40 }),\n `→ ${ANSIColors.RESET}`,\n ].join('');\n\n const prefixText = `${ANSIColors.GREY_DARK}[${formatPath(baseFilePath)}${ANSIColors.GREY_DARK}][${formatLocale(locale)}${ANSIColors.GREY_DARK}] `;\n const prefix = [\n colon(prefixText, { colSize: 40 }),\n `→ ${ANSIColors.RESET}`,\n ].join('');\n\n // Chunking\n const chunks = chunkText(fileContent);\n appLogger(\n `${filePrefix}Base file splitted into ${colorizeNumber(chunks.length)} chunks`\n );\n\n // Instead of replacing content in a string, we push to an array\n const translatedParts: string[] = [];\n\n // Prepare Base Prompt\n const basePrompt = readAsset('./prompts/TRANSLATE_PROMPT.md', 'utf-8')\n .replaceAll('{{localeName}}', `${formatLocale(locale, false)}`)\n .replaceAll('{{baseLocaleName}}', `${formatLocale(baseLocale, false)}`)\n .replace('{{applicationContext}}', aiOptions?.applicationContext ?? '-')\n .replace('{{customInstructions}}', customInstructions ?? '-');\n\n // Iterate and Translate\n for await (const [i, chunk] of chunks.entries()) {\n // Circuit Breaker Check inside the loop (in case error happened elsewhere while processing)\n if (errorState.shouldStop) return false;\n\n const isFirstChunk = i === 0;\n const fileToTranslateCurrentChunk = chunk.content;\n\n // Build the chunk-specific prompt\n const getPrevChunkPrompt = () =>\n `**CHUNK ${i} of ${chunks.length}** that has been translated in ${formatLocale(locale)}:\\n` +\n `///chunkStart///` +\n getChunk(translatedParts.join(''), chunks[i - 1]) +\n `///chunkEnd///`;\n\n const getBaseChunkContextPrompt = () =>\n `**CHUNK ${i + 1} to ${Math.min(i + 3, chunks.length)} of ${chunks.length}** is the base chunk in ${formatLocale(baseLocale, false)} as reference.\\n` +\n `///chunksStart///` +\n (chunks[i - 1]?.content ?? '') +\n chunks[i].content +\n (chunks[i + 1]?.content ?? '') +\n `///chunksEnd///`;\n\n // Make the actual translation call\n const chunkTranslation = await retryManager(async () => {\n const result = await chunkInference(\n [\n { role: 'system', content: basePrompt },\n { role: 'system', content: getBaseChunkContextPrompt() },\n ...(isFirstChunk\n ? []\n : [{ role: 'system', content: getPrevChunkPrompt() } as const]),\n {\n role: 'system',\n content: `The next user message will be the **CHUNK ${colorizeNumber(i + 1)} of ${colorizeNumber(chunks.length)}** in ${formatLocale(baseLocale, false)} to translate in ${formatLocale(locale, false)}.\\n\n STRICT INSTRUCTIONS:\n 1. Translate ONLY the content of this specific chunk. \n 2. Do NOT repeat the content from the previous chunk.\n 3. Start the translation exactly where the previous chunk ended.\n 4. Preserve all code blocks and formatting exactly.`,\n },\n { role: 'user', content: fileToTranslateCurrentChunk },\n ],\n aiOptions,\n configuration,\n aiClient,\n aiConfig\n );\n\n appLogger(\n [\n `${prefix}`,\n `${ANSIColors.GREY_DARK}[Chunk `,\n colorizeNumber(i + 1),\n `${ANSIColors.GREY_DARK} of `,\n colorizeNumber(chunks.length),\n `${ANSIColors.GREY_DARK}] →${ANSIColors.RESET} `,\n `${colorizeNumber(result.tokenUsed)} tokens used`,\n ].join('')\n );\n\n const fixedTranslatedChunkResult = fixChunkStartEndChars(\n result?.fileContent,\n fileToTranslateCurrentChunk\n );\n\n return fixedTranslatedChunkResult;\n })();\n\n // Replace the chunk in the file content\n fileResultContent = fileResultContent.replace(\n fileToTranslateCurrentChunk,\n chunkTranslation\n );\n }\n\n // Write final file by joining parts\n const finalContent = translatedParts.join('');\n\n mkdirSync(dirname(outputFilePath), { recursive: true });\n writeFileSync(outputFilePath, finalContent);\n\n const relativePath = relative(\n configuration.content.baseDir,\n outputFilePath\n );\n\n appLogger(\n `${colorize('✔', ANSIColors.GREEN)} File ${formatPath(relativePath)} created/updated successfully.`\n );\n\n return true; // Success\n } catch (error: any) {\n // Handle Errors and Update State\n\n errorState.count++;\n\n // If it's an Access Denied error, stop immediately (set count to max)\n const errorString = JSON.stringify(error);\n const errorMessage = error?.message ?? '';\n if (\n errorString.includes('AI_ACCESS_DENIED') ||\n errorMessage.includes('Access keys') ||\n errorMessage.includes('Access denied') ||\n errorMessage.includes('Invalid Access keys')\n ) {\n errorState.count = errorState.maxErrors + 1;\n appLogger(\n `${colorize('✖', ANSIColors.RED)} Critical Authentication Error. Aborting all tasks.`\n );\n }\n\n if (errorState.count >= errorState.maxErrors && !errorState.shouldStop) {\n errorState.shouldStop = true;\n appLogger(\n `${colorize('✖', ANSIColors.RED)} Too many errors (${errorState.count}). Stopping process.`\n );\n }\n\n return false; // Failed\n }\n};\n\ntype TranslateDocOptions = {\n docPattern: string[];\n locales: Locale[];\n excludedGlobPattern: string[];\n baseLocale: Locale;\n aiOptions?: AIOptions;\n nbSimultaneousFileProcessed?: number;\n configOptions?: GetConfigurationOptions;\n customInstructions?: string;\n skipIfModifiedBefore?: number | string | Date;\n skipIfModifiedAfter?: number | string | Date;\n skipIfExists?: boolean;\n gitOptions?: ListGitFilesOptions;\n};\n\n/**\n * Main translate function: scans all .md files in \"en/\" (unless you specified DOC_LIST),\n * then translates them to each locale in LOCALE_LIST.\n */\nexport const translateDoc = async ({\n docPattern,\n locales,\n excludedGlobPattern,\n baseLocale,\n aiOptions,\n nbSimultaneousFileProcessed,\n configOptions,\n customInstructions,\n skipIfModifiedBefore,\n skipIfModifiedAfter,\n skipIfExists,\n gitOptions,\n}: TranslateDocOptions) => {\n const configuration = getConfiguration(configOptions);\n const appLogger = getAppLogger(configuration);\n\n if (nbSimultaneousFileProcessed && nbSimultaneousFileProcessed > 10) {\n appLogger(\n `Warning: nbSimultaneousFileProcessed is set to ${nbSimultaneousFileProcessed}, which is greater than 10. Setting it to 10.`\n );\n nbSimultaneousFileProcessed = 10; // Limit the number of simultaneous file processed to 10\n }\n\n let docList: string[] = await fg(docPattern, {\n ignore: excludedGlobPattern,\n });\n\n const aiResult = await setupAI(configuration, aiOptions);\n\n if (!aiResult?.hasAIAccess) return;\n\n const { aiClient, aiConfig } = aiResult;\n\n if (gitOptions) {\n const gitChangedFiles = await listGitFiles(gitOptions);\n\n if (gitChangedFiles) {\n // Convert dictionary file paths to be relative to git root for comparison\n\n // Filter dictionaries based on git changed files\n docList = docList.filter((path) =>\n gitChangedFiles.some((gitFile) => join(process.cwd(), path) === gitFile)\n );\n }\n }\n\n // OAuth handled by API proxy internally\n\n appLogger(`Base locale is ${formatLocale(baseLocale)}`);\n appLogger(\n `Translating ${colorizeNumber(locales.length)} locales: [ ${formatLocale(locales)} ]`\n );\n\n appLogger(`Translating ${colorizeNumber(docList.length)} files:`);\n docList.forEach((path) => {\n appLogger(` - ${formatPath(path)}`);\n });\n\n // Initialize Error State\n const MAX_ALLOWED_ERRORS = 5;\n const errorState: ErrorState = {\n count: 0,\n maxErrors: MAX_ALLOWED_ERRORS,\n shouldStop: false,\n };\n\n // Create all tasks to be processed\n const allTasks = docList.flatMap((docPath) =>\n locales.map((locale) => async () => {\n // Early exit if too many errors\n if (errorState.shouldStop) return;\n\n appLogger(\n `Translating file: ${formatPath(docPath)} to ${formatLocale(locale)}`\n );\n\n const absoluteBaseFilePath = join(configuration.content.baseDir, docPath);\n const outputFilePath = getOutputFilePath(\n absoluteBaseFilePath,\n locale,\n baseLocale\n );\n\n // Skip if file exists and skipIfExists option is enabled\n if (skipIfExists && existsSync(outputFilePath)) {\n const relativePath = relative(\n configuration.content.baseDir,\n outputFilePath\n );\n appLogger(\n `${colorize('⊘', ANSIColors.YELLOW)} File ${formatPath(relativePath)} already exists, skipping.`\n );\n return;\n }\n\n // check if the file exist, otherwise create it\n if (!existsSync(outputFilePath)) {\n const relativePath = relative(\n configuration.content.baseDir,\n outputFilePath\n );\n appLogger(\n `File ${formatPath(relativePath)} does not exist, creating it...`\n );\n mkdirSync(dirname(outputFilePath), { recursive: true });\n writeFileSync(outputFilePath, '');\n }\n\n const fileModificationData = checkFileModifiedRange(outputFilePath, {\n skipIfModifiedBefore,\n skipIfModifiedAfter,\n });\n\n if (fileModificationData.isSkipped) {\n appLogger(fileModificationData.message);\n return;\n }\n\n // Call translateFile with errorState\n await translateFile(\n absoluteBaseFilePath,\n outputFilePath,\n locale as Locale,\n baseLocale,\n configuration,\n errorState,\n aiOptions,\n customInstructions,\n aiClient,\n aiConfig\n );\n })\n );\n\n await parallelize(\n allTasks,\n (task) => task(),\n nbSimultaneousFileProcessed ?? 3\n );\n\n if (errorState.count > 0) {\n appLogger(\n `Process finished with ${colorizeNumber(errorState.count)} error${errorState.count === 1 ? '' : 's'}.`\n );\n }\n};\n"],"mappings":"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"}
@@ -1,26 +1,52 @@
1
+ import { checkConfigConsistency } from "./checkConfigConsistency.mjs";
1
2
  import { getIntlayerAPIProxy } from "@intlayer/api";
2
3
  import { ANSIColors, colorize, extractErrorMessage, getAppLogger } from "@intlayer/config";
3
4
 
4
5
  //#region src/utils/checkAccess.ts
5
- const checkCMSAuth = async (configuration) => {
6
+ const checkCMSAuth = async (configuration, shouldCheckConfigConsistency = true) => {
6
7
  const appLogger = getAppLogger(configuration, { config: { prefix: "" } });
7
8
  if (!(configuration.editor.clientId && configuration.editor.clientSecret)) {
8
- appLogger("CMS auth not provided.", { level: "error" });
9
+ appLogger([
10
+ "CMS auth not provided. You can either retreive the CMS access key on",
11
+ colorize("https://intlayer.org/dahboard", ANSIColors.GREY),
12
+ colorize("(see doc:", ANSIColors.GREY_DARK),
13
+ colorize("https://intlayer.org/doc/concept/cms", ANSIColors.GREY),
14
+ colorize(")", ANSIColors.GREY_DARK),
15
+ "."
16
+ ], { level: "error" });
9
17
  return false;
10
18
  }
11
19
  const intlayerAPI = getIntlayerAPIProxy(void 0, configuration);
12
20
  try {
13
- await intlayerAPI.oAuth.getOAuth2AccessToken();
21
+ const project = (await intlayerAPI.oAuth.getOAuth2AccessToken()).data?.project;
22
+ if (!project) {
23
+ appLogger("Project not found");
24
+ return true;
25
+ }
26
+ if (project.configuration && shouldCheckConfigConsistency) try {
27
+ checkConfigConsistency(project.configuration, configuration);
28
+ } catch {
29
+ appLogger([
30
+ "Remote configuration is not up to date. The project configuration does not match the local configuration.",
31
+ "You can push the configuration by running",
32
+ colorize("npx intlayer push", ANSIColors.CYAN),
33
+ colorize("(see doc:", ANSIColors.GREY_DARK),
34
+ colorize("https://intlayer.org/doc/concept/cli/push", ANSIColors.GREY),
35
+ colorize(")", ANSIColors.GREY_DARK),
36
+ "."
37
+ ], { level: "warn" });
38
+ }
14
39
  } catch (error) {
15
40
  appLogger(extractErrorMessage(error), { level: "error" });
16
41
  return false;
17
42
  }
18
43
  return true;
19
44
  };
20
- const checkAIAccess = async (configuration, aiOptions) => {
45
+ const checkAIAccess = async (configuration, aiOptions, shouldCheckConfigConsistency = true) => {
21
46
  const appLogger = getAppLogger(configuration);
22
47
  const hasCMSAuth = Boolean(configuration.editor.clientId && configuration.editor.clientSecret);
23
- if (Boolean(configuration.ai?.apiKey || aiOptions?.apiKey)) return true;
48
+ const isOllama = configuration.ai?.provider === "ollama" || aiOptions?.provider === "ollama";
49
+ if (Boolean(configuration.ai?.apiKey || aiOptions?.apiKey) || isOllama) return true;
24
50
  if (!hasCMSAuth) {
25
51
  appLogger([
26
52
  "AI options or API key not provided. You can either retreive the CMS access key on",
@@ -36,7 +62,7 @@ const checkAIAccess = async (configuration, aiOptions) => {
36
62
  ], { level: "error" });
37
63
  return false;
38
64
  }
39
- return await checkCMSAuth(configuration);
65
+ return await checkCMSAuth(configuration, shouldCheckConfigConsistency);
40
66
  };
41
67
 
42
68
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"checkAccess.mjs","names":[],"sources":["../../../src/utils/checkAccess.ts"],"sourcesContent":["import type { AIOptions } from '@intlayer/api';\nimport { getIntlayerAPIProxy } from '@intlayer/api';\nimport {\n ANSIColors,\n colorize,\n extractErrorMessage,\n getAppLogger,\n} from '@intlayer/config';\nimport type { IntlayerConfig } from '@intlayer/types';\n\nexport const checkCMSAuth = async (\n configuration: IntlayerConfig\n): Promise<boolean> => {\n const appLogger = getAppLogger(configuration, {\n config: {\n prefix: '',\n },\n });\n\n const hasCMSAuth =\n configuration.editor.clientId && configuration.editor.clientSecret;\n if (!hasCMSAuth) {\n appLogger('CMS auth not provided.', {\n level: 'error',\n });\n\n return false;\n }\n const intlayerAPI = getIntlayerAPIProxy(undefined, configuration);\n\n try {\n await intlayerAPI.oAuth.getOAuth2AccessToken();\n } catch (error) {\n const message = extractErrorMessage(error);\n\n appLogger(message, {\n level: 'error',\n });\n return false;\n }\n\n return true;\n};\n\nexport const checkAIAccess = async (\n configuration: IntlayerConfig,\n aiOptions?: AIOptions\n): Promise<boolean> => {\n const appLogger = getAppLogger(configuration);\n\n const hasCMSAuth = Boolean(\n configuration.editor.clientId && configuration.editor.clientSecret\n );\n const hasHisOwnAIAPIKey = Boolean(\n configuration.ai?.apiKey || aiOptions?.apiKey\n );\n\n if (hasHisOwnAIAPIKey) {\n return true;\n }\n\n // User need to provide either his own AI API key or the CMS auth\n if (!hasCMSAuth) {\n appLogger(\n [\n 'AI options or API key not provided. You can either retreive the CMS access key on',\n colorize('https://intlayer.org/dahboard', ANSIColors.GREY),\n colorize('(see doc:', ANSIColors.GREY_DARK),\n colorize('https://intlayer.org/doc/concept/cms', ANSIColors.GREY),\n colorize(')', ANSIColors.GREY_DARK),\n '. Alternatively, you can add your own OpenAI API key in the settings',\n colorize('(see doc:', ANSIColors.GREY_DARK),\n colorize(\n 'https://intlayer.org/doc/concept/configuration',\n ANSIColors.GREY\n ),\n colorize(')', ANSIColors.GREY_DARK),\n '.',\n ],\n {\n level: 'error',\n }\n );\n\n return false;\n }\n\n // If the user do not have his own AI API key, we need to check the CMS auth\n return await checkCMSAuth(configuration);\n};\n"],"mappings":";;;;AAUA,MAAa,eAAe,OAC1B,kBACqB;CACrB,MAAM,YAAY,aAAa,eAAe,EAC5C,QAAQ,EACN,QAAQ,IACT,EACF,CAAC;AAIF,KAAI,EADF,cAAc,OAAO,YAAY,cAAc,OAAO,eACvC;AACf,YAAU,0BAA0B,EAClC,OAAO,SACR,CAAC;AAEF,SAAO;;CAET,MAAM,cAAc,oBAAoB,QAAW,cAAc;AAEjE,KAAI;AACF,QAAM,YAAY,MAAM,sBAAsB;UACvC,OAAO;AAGd,YAFgB,oBAAoB,MAAM,EAEvB,EACjB,OAAO,SACR,CAAC;AACF,SAAO;;AAGT,QAAO;;AAGT,MAAa,gBAAgB,OAC3B,eACA,cACqB;CACrB,MAAM,YAAY,aAAa,cAAc;CAE7C,MAAM,aAAa,QACjB,cAAc,OAAO,YAAY,cAAc,OAAO,aACvD;AAKD,KAJ0B,QACxB,cAAc,IAAI,UAAU,WAAW,OACxC,CAGC,QAAO;AAIT,KAAI,CAAC,YAAY;AACf,YACE;GACE;GACA,SAAS,iCAAiC,WAAW,KAAK;GAC1D,SAAS,aAAa,WAAW,UAAU;GAC3C,SAAS,wCAAwC,WAAW,KAAK;GACjE,SAAS,KAAK,WAAW,UAAU;GACnC;GACA,SAAS,aAAa,WAAW,UAAU;GAC3C,SACE,kDACA,WAAW,KACZ;GACD,SAAS,KAAK,WAAW,UAAU;GACnC;GACD,EACD,EACE,OAAO,SACR,CACF;AAED,SAAO;;AAIT,QAAO,MAAM,aAAa,cAAc"}
1
+ {"version":3,"file":"checkAccess.mjs","names":[],"sources":["../../../src/utils/checkAccess.ts"],"sourcesContent":["import type { AIOptions } from '@intlayer/api';\nimport { getIntlayerAPIProxy } from '@intlayer/api';\nimport {\n ANSIColors,\n colorize,\n extractErrorMessage,\n getAppLogger,\n} from '@intlayer/config';\nimport type { IntlayerConfig } from '@intlayer/types';\nimport { checkConfigConsistency } from './checkConfigConsistency';\n\nexport const checkCMSAuth = async (\n configuration: IntlayerConfig,\n shouldCheckConfigConsistency: boolean = true\n): Promise<boolean> => {\n const appLogger = getAppLogger(configuration, {\n config: {\n prefix: '',\n },\n });\n\n const hasCMSAuth =\n configuration.editor.clientId && configuration.editor.clientSecret;\n if (!hasCMSAuth) {\n appLogger(\n [\n 'CMS auth not provided. You can either retreive the CMS access key on',\n colorize('https://intlayer.org/dahboard', ANSIColors.GREY),\n colorize('(see doc:', ANSIColors.GREY_DARK),\n colorize('https://intlayer.org/doc/concept/cms', ANSIColors.GREY),\n colorize(')', ANSIColors.GREY_DARK),\n '.',\n ],\n {\n level: 'error',\n }\n );\n\n return false;\n }\n const intlayerAPI = getIntlayerAPIProxy(undefined, configuration);\n\n try {\n const result = await intlayerAPI.oAuth.getOAuth2AccessToken();\n\n const project = result.data?.project;\n\n if (!project) {\n appLogger('Project not found');\n\n return true;\n }\n\n if (project.configuration && shouldCheckConfigConsistency) {\n try {\n // Recursively check if project.configuration (subset) matches configuration (superset)\n checkConfigConsistency(project.configuration, configuration);\n } catch {\n appLogger(\n [\n 'Remote configuration is not up to date. The project configuration does not match the local configuration.',\n 'You can push the configuration by running',\n colorize('npx intlayer push', ANSIColors.CYAN),\n colorize('(see doc:', ANSIColors.GREY_DARK),\n colorize(\n 'https://intlayer.org/doc/concept/cli/push',\n ANSIColors.GREY\n ),\n colorize(')', ANSIColors.GREY_DARK),\n '.',\n ],\n {\n level: 'warn',\n }\n );\n }\n }\n } catch (error) {\n const message = extractErrorMessage(error);\n\n appLogger(message, {\n level: 'error',\n });\n return false;\n }\n\n return true;\n};\n\nexport const checkAIAccess = async (\n configuration: IntlayerConfig,\n aiOptions?: AIOptions,\n shouldCheckConfigConsistency: boolean = true\n): Promise<boolean> => {\n const appLogger = getAppLogger(configuration);\n\n const hasCMSAuth = Boolean(\n configuration.editor.clientId && configuration.editor.clientSecret\n );\n const isOllama =\n configuration.ai?.provider === 'ollama' || aiOptions?.provider === 'ollama';\n const hasHisOwnAIAPIKey = Boolean(\n configuration.ai?.apiKey || aiOptions?.apiKey\n );\n\n if (hasHisOwnAIAPIKey || isOllama) {\n return true;\n }\n\n // User need to provide either his own AI API key or the CMS auth\n if (!hasCMSAuth) {\n appLogger(\n [\n 'AI options or API key not provided. You can either retreive the CMS access key on',\n colorize('https://intlayer.org/dahboard', ANSIColors.GREY),\n colorize('(see doc:', ANSIColors.GREY_DARK),\n colorize('https://intlayer.org/doc/concept/cms', ANSIColors.GREY),\n colorize(')', ANSIColors.GREY_DARK),\n '. Alternatively, you can add your own OpenAI API key in the settings',\n colorize('(see doc:', ANSIColors.GREY_DARK),\n colorize(\n 'https://intlayer.org/doc/concept/configuration',\n ANSIColors.GREY\n ),\n colorize(')', ANSIColors.GREY_DARK),\n '.',\n ],\n {\n level: 'error',\n }\n );\n\n return false;\n }\n\n // If the user do not have his own AI API key, we need to check the CMS auth\n return await checkCMSAuth(configuration, shouldCheckConfigConsistency);\n};\n"],"mappings":";;;;;AAWA,MAAa,eAAe,OAC1B,eACA,+BAAwC,SACnB;CACrB,MAAM,YAAY,aAAa,eAAe,EAC5C,QAAQ,EACN,QAAQ,IACT,EACF,CAAC;AAIF,KAAI,EADF,cAAc,OAAO,YAAY,cAAc,OAAO,eACvC;AACf,YACE;GACE;GACA,SAAS,iCAAiC,WAAW,KAAK;GAC1D,SAAS,aAAa,WAAW,UAAU;GAC3C,SAAS,wCAAwC,WAAW,KAAK;GACjE,SAAS,KAAK,WAAW,UAAU;GACnC;GACD,EACD,EACE,OAAO,SACR,CACF;AAED,SAAO;;CAET,MAAM,cAAc,oBAAoB,QAAW,cAAc;AAEjE,KAAI;EAGF,MAAM,WAFS,MAAM,YAAY,MAAM,sBAAsB,EAEtC,MAAM;AAE7B,MAAI,CAAC,SAAS;AACZ,aAAU,oBAAoB;AAE9B,UAAO;;AAGT,MAAI,QAAQ,iBAAiB,6BAC3B,KAAI;AAEF,0BAAuB,QAAQ,eAAe,cAAc;UACtD;AACN,aACE;IACE;IACA;IACA,SAAS,qBAAqB,WAAW,KAAK;IAC9C,SAAS,aAAa,WAAW,UAAU;IAC3C,SACE,6CACA,WAAW,KACZ;IACD,SAAS,KAAK,WAAW,UAAU;IACnC;IACD,EACD,EACE,OAAO,QACR,CACF;;UAGE,OAAO;AAGd,YAFgB,oBAAoB,MAAM,EAEvB,EACjB,OAAO,SACR,CAAC;AACF,SAAO;;AAGT,QAAO;;AAGT,MAAa,gBAAgB,OAC3B,eACA,WACA,+BAAwC,SACnB;CACrB,MAAM,YAAY,aAAa,cAAc;CAE7C,MAAM,aAAa,QACjB,cAAc,OAAO,YAAY,cAAc,OAAO,aACvD;CACD,MAAM,WACJ,cAAc,IAAI,aAAa,YAAY,WAAW,aAAa;AAKrE,KAJ0B,QACxB,cAAc,IAAI,UAAU,WAAW,OACxC,IAEwB,SACvB,QAAO;AAIT,KAAI,CAAC,YAAY;AACf,YACE;GACE;GACA,SAAS,iCAAiC,WAAW,KAAK;GAC1D,SAAS,aAAa,WAAW,UAAU;GAC3C,SAAS,wCAAwC,WAAW,KAAK;GACjE,SAAS,KAAK,WAAW,UAAU;GACnC;GACA,SAAS,aAAa,WAAW,UAAU;GAC3C,SACE,kDACA,WAAW,KACZ;GACD,SAAS,KAAK,WAAW,UAAU;GACnC;GACD,EACD,EACE,OAAO,SACR,CACF;AAED,SAAO;;AAIT,QAAO,MAAM,aAAa,eAAe,6BAA6B"}
@@ -0,0 +1,23 @@
1
+ import { isDeepStrictEqual } from "node:util";
2
+
3
+ //#region src/utils/checkConfigConsistency.ts
4
+ /**
5
+ * Recursively checks if a subset configuration matches a superset configuration.
6
+ * Throws an error if any value in the subset doesn't match the corresponding value in the superset.
7
+ *
8
+ * @param subset - The subset configuration (e.g., project.configuration from CMS)
9
+ * @param superset - The superset configuration (e.g., local configuration)
10
+ * @throws Error if any value in subset doesn't match the corresponding value in superset
11
+ */
12
+ const checkConfigConsistency = (subset, superset) => {
13
+ Object.keys(subset).forEach((key) => {
14
+ const isSubsetObject = typeof subset[key] === "object" && subset[key] !== null && !Array.isArray(subset[key]);
15
+ const isSupersetObject = typeof superset[key] === "object" && superset[key] !== null && !Array.isArray(superset[key]);
16
+ if (isSubsetObject && isSupersetObject) checkConfigConsistency(subset[key], superset[key]);
17
+ else if (!isDeepStrictEqual(subset[key], superset[key])) throw new Error(`Configuration mismatch at key "${key}": expected ${JSON.stringify(superset[key])}, got ${JSON.stringify(subset[key])}`);
18
+ });
19
+ };
20
+
21
+ //#endregion
22
+ export { checkConfigConsistency };
23
+ //# sourceMappingURL=checkConfigConsistency.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"checkConfigConsistency.mjs","names":[],"sources":["../../../src/utils/checkConfigConsistency.ts"],"sourcesContent":["import { isDeepStrictEqual } from 'node:util';\n\n/**\n * Recursively checks if a subset configuration matches a superset configuration.\n * Throws an error if any value in the subset doesn't match the corresponding value in the superset.\n *\n * @param subset - The subset configuration (e.g., project.configuration from CMS)\n * @param superset - The superset configuration (e.g., local configuration)\n * @throws Error if any value in subset doesn't match the corresponding value in superset\n */\nexport const checkConfigConsistency = (\n subset: Record<string, any>,\n superset: Record<string, any>\n): void => {\n Object.keys(subset).forEach((key) => {\n const isSubsetObject =\n typeof subset[key] === 'object' &&\n subset[key] !== null &&\n !Array.isArray(subset[key]);\n const isSupersetObject =\n typeof superset[key] === 'object' &&\n superset[key] !== null &&\n !Array.isArray(superset[key]);\n\n if (isSubsetObject && isSupersetObject) {\n checkConfigConsistency(subset[key], superset[key]);\n } else {\n if (!isDeepStrictEqual(subset[key], superset[key])) {\n throw new Error(\n `Configuration mismatch at key \"${key}\": expected ${JSON.stringify(superset[key])}, got ${JSON.stringify(subset[key])}`\n );\n }\n }\n });\n};\n"],"mappings":";;;;;;;;;;;AAUA,MAAa,0BACX,QACA,aACS;AACT,QAAO,KAAK,OAAO,CAAC,SAAS,QAAQ;EACnC,MAAM,iBACJ,OAAO,OAAO,SAAS,YACvB,OAAO,SAAS,QAChB,CAAC,MAAM,QAAQ,OAAO,KAAK;EAC7B,MAAM,mBACJ,OAAO,SAAS,SAAS,YACzB,SAAS,SAAS,QAClB,CAAC,MAAM,QAAQ,SAAS,KAAK;AAE/B,MAAI,kBAAkB,iBACpB,wBAAuB,OAAO,MAAM,SAAS,KAAK;WAE9C,CAAC,kBAAkB,OAAO,MAAM,SAAS,KAAK,CAChD,OAAM,IAAI,MACR,kCAAkC,IAAI,cAAc,KAAK,UAAU,SAAS,KAAK,CAAC,QAAQ,KAAK,UAAU,OAAO,KAAK,GACtH;GAGL"}
@@ -20,28 +20,37 @@ const logAIConfig = (aiOptions, appLogger) => {
20
20
  */
21
21
  const setupAI = async (configuration, aiOptions) => {
22
22
  const appLogger = getAppLogger(configuration);
23
- const hasAIAccess = await checkAIAccess(configuration, aiOptions);
24
- if (aiOptions?.apiKey) try {
25
- const aiClient = await import("@intlayer/ai");
23
+ if (aiOptions?.apiKey || aiOptions?.provider === "ollama" || configuration.ai?.apiKey || configuration.ai?.provider === "ollama") {
24
+ let aiClient;
25
+ try {
26
+ aiClient = await import("@intlayer/ai");
27
+ } catch {
28
+ appLogger([
29
+ colorize("Using your API key, you can install the", ANSIColors.GREY),
30
+ colorize("@intlayer/ai", ANSIColors.GREY_LIGHT),
31
+ colorize("package to run the process locally, with no dependency on the Intlayer server", ANSIColors.GREY)
32
+ ], { level: "warn" });
33
+ const hasAIAccess$1 = await checkAIAccess(configuration, aiOptions);
34
+ logAIConfig(aiOptions ?? {}, appLogger);
35
+ return {
36
+ isCustomAI: false,
37
+ hasAIAccess: hasAIAccess$1
38
+ };
39
+ }
26
40
  appLogger([colorize("@intlayer/ai", ANSIColors.GREY_LIGHT), colorize("found - Run process locally", ANSIColors.GREY_DARK)]);
27
41
  const aiConfig = await aiClient.getAIConfig({
28
42
  userOptions: aiOptions,
29
43
  accessType: ["public"]
30
44
  });
31
- logAIConfig(aiOptions, appLogger);
45
+ logAIConfig(aiOptions ?? {}, appLogger);
32
46
  return {
33
47
  aiClient,
34
48
  aiConfig,
35
49
  isCustomAI: true,
36
- hasAIAccess
50
+ hasAIAccess: true
37
51
  };
38
- } catch {
39
- appLogger([
40
- colorize("Using your API key, you can install the", ANSIColors.GREY),
41
- colorize("@intlayer/ai", ANSIColors.GREY_LIGHT),
42
- colorize("package to run the process locally, with no dependency on the Intlayer server", ANSIColors.GREY)
43
- ], { level: "warn" });
44
52
  }
53
+ const hasAIAccess = await checkAIAccess(configuration, aiOptions);
45
54
  logAIConfig(aiOptions ?? {}, appLogger);
46
55
  return {
47
56
  isCustomAI: false,
@@ -1 +1 @@
1
- {"version":3,"file":"setupAI.mjs","names":[],"sources":["../../../src/utils/setupAI.ts"],"sourcesContent":["import type { AIConfig, AIOptions } from '@intlayer/ai';\nimport {\n ANSIColors,\n colorize,\n getAppLogger,\n type logger,\n} from '@intlayer/config';\nimport type { IntlayerConfig } from '@intlayer/types';\nimport { checkAIAccess } from './checkAccess';\n\nexport type AIClient = typeof import('@intlayer/ai');\n\ntype SetupAIResult = {\n aiClient?: AIClient;\n aiConfig?: AIConfig;\n isCustomAI: boolean;\n hasAIAccess: boolean;\n};\n\n// Disable warnings from the AI SDK\nglobalThis.AI_SDK_LOG_WARNINGS = false;\n\nconst logAIConfig = (aiOptions: AIOptions, appLogger: typeof logger) => {\n appLogger([\n colorize('Provider:', ANSIColors.GREY_DARK),\n colorize(aiOptions?.provider ?? '(default)', ANSIColors.BLUE),\n colorize('- Model:', ANSIColors.GREY_DARK),\n colorize(aiOptions?.model ?? '(default)', ANSIColors.BLUE),\n colorize('- API Key:', ANSIColors.GREY_DARK),\n colorize(aiOptions?.apiKey ? '✓' : '(not set)', ANSIColors.BLUE),\n ]);\n};\n\n/**\n * Checks if the @intlayer/ai package is available and configured when an API key is provided.\n * If API key is present but package is missing, logs a warning.\n * Also checks if the user has access to AI (either via local key or CMS auth).\n */\nexport const setupAI = async (\n configuration: IntlayerConfig,\n aiOptions?: AIOptions\n): Promise<SetupAIResult | undefined> => {\n const appLogger = getAppLogger(configuration);\n\n const hasAIAccess = await checkAIAccess(configuration, aiOptions);\n\n if (aiOptions?.apiKey) {\n try {\n // Dynamically import the AI package if an API key is provided\n const aiClient = await import('@intlayer/ai');\n\n appLogger([\n colorize('@intlayer/ai', ANSIColors.GREY_LIGHT),\n colorize('found - Run process locally', ANSIColors.GREY_DARK),\n ]);\n\n const aiConfig = await aiClient.getAIConfig({\n userOptions: aiOptions,\n accessType: ['public'],\n });\n\n logAIConfig(aiOptions, appLogger);\n\n return {\n aiClient,\n aiConfig,\n isCustomAI: true,\n hasAIAccess,\n };\n } catch {\n appLogger(\n [\n colorize('Using your API key, you can install the', ANSIColors.GREY),\n colorize('@intlayer/ai', ANSIColors.GREY_LIGHT),\n colorize(\n 'package to run the process locally, with no dependency on the Intlayer server',\n ANSIColors.GREY\n ),\n ],\n {\n level: 'warn',\n }\n );\n }\n }\n\n logAIConfig(aiOptions ?? {}, appLogger);\n\n return {\n isCustomAI: false,\n hasAIAccess,\n };\n};\n"],"mappings":";;;;AAoBA,WAAW,sBAAsB;AAEjC,MAAM,eAAe,WAAsB,cAA6B;AACtE,WAAU;EACR,SAAS,aAAa,WAAW,UAAU;EAC3C,SAAS,WAAW,YAAY,aAAa,WAAW,KAAK;EAC7D,SAAS,YAAY,WAAW,UAAU;EAC1C,SAAS,WAAW,SAAS,aAAa,WAAW,KAAK;EAC1D,SAAS,cAAc,WAAW,UAAU;EAC5C,SAAS,WAAW,SAAS,MAAM,aAAa,WAAW,KAAK;EACjE,CAAC;;;;;;;AAQJ,MAAa,UAAU,OACrB,eACA,cACuC;CACvC,MAAM,YAAY,aAAa,cAAc;CAE7C,MAAM,cAAc,MAAM,cAAc,eAAe,UAAU;AAEjE,KAAI,WAAW,OACb,KAAI;EAEF,MAAM,WAAW,MAAM,OAAO;AAE9B,YAAU,CACR,SAAS,gBAAgB,WAAW,WAAW,EAC/C,SAAS,+BAA+B,WAAW,UAAU,CAC9D,CAAC;EAEF,MAAM,WAAW,MAAM,SAAS,YAAY;GAC1C,aAAa;GACb,YAAY,CAAC,SAAS;GACvB,CAAC;AAEF,cAAY,WAAW,UAAU;AAEjC,SAAO;GACL;GACA;GACA,YAAY;GACZ;GACD;SACK;AACN,YACE;GACE,SAAS,2CAA2C,WAAW,KAAK;GACpE,SAAS,gBAAgB,WAAW,WAAW;GAC/C,SACE,iFACA,WAAW,KACZ;GACF,EACD,EACE,OAAO,QACR,CACF;;AAIL,aAAY,aAAa,EAAE,EAAE,UAAU;AAEvC,QAAO;EACL,YAAY;EACZ;EACD"}
1
+ {"version":3,"file":"setupAI.mjs","names":["aiClient: AIClient | undefined","hasAIAccess"],"sources":["../../../src/utils/setupAI.ts"],"sourcesContent":["import type { AIConfig, AIOptions } from '@intlayer/ai';\nimport {\n ANSIColors,\n colorize,\n getAppLogger,\n type logger,\n} from '@intlayer/config';\nimport type { IntlayerConfig } from '@intlayer/types';\nimport { checkAIAccess } from './checkAccess';\n\nexport type AIClient = typeof import('@intlayer/ai');\n\ntype SetupAIResult = {\n aiClient?: AIClient;\n aiConfig?: AIConfig;\n isCustomAI: boolean;\n hasAIAccess: boolean;\n};\n\n// Disable warnings from the AI SDK\nglobalThis.AI_SDK_LOG_WARNINGS = false;\n\nconst logAIConfig = (aiOptions: AIOptions, appLogger: typeof logger) => {\n appLogger([\n colorize('Provider:', ANSIColors.GREY_DARK),\n colorize(aiOptions?.provider ?? '(default)', ANSIColors.BLUE),\n colorize('- Model:', ANSIColors.GREY_DARK),\n colorize(aiOptions?.model ?? '(default)', ANSIColors.BLUE),\n colorize('- API Key:', ANSIColors.GREY_DARK),\n colorize(aiOptions?.apiKey ? '✓' : '(not set)', ANSIColors.BLUE),\n ]);\n};\n\n/**\n * Checks if the @intlayer/ai package is available and configured when an API key is provided.\n * If API key is present but package is missing, logs a warning.\n * Also checks if the user has access to AI (either via local key or CMS auth).\n */\nexport const setupAI = async (\n configuration: IntlayerConfig,\n aiOptions?: AIOptions\n): Promise<SetupAIResult | undefined> => {\n const appLogger = getAppLogger(configuration);\n\n const isLocalAI =\n aiOptions?.apiKey ||\n aiOptions?.provider === 'ollama' ||\n configuration.ai?.apiKey ||\n configuration.ai?.provider === 'ollama';\n\n if (isLocalAI) {\n // Try to import the AI package for local AI usage\n let aiClient: AIClient | undefined;\n\n try {\n aiClient = await import('@intlayer/ai');\n } catch {\n // Package not installed - log warning and fall back to backend\n appLogger(\n [\n colorize('Using your API key, you can install the', ANSIColors.GREY),\n colorize('@intlayer/ai', ANSIColors.GREY_LIGHT),\n colorize(\n 'package to run the process locally, with no dependency on the Intlayer server',\n ANSIColors.GREY\n ),\n ],\n {\n level: 'warn',\n }\n );\n\n // Fall back to backend API check\n const hasAIAccess = await checkAIAccess(configuration, aiOptions);\n logAIConfig(aiOptions ?? {}, appLogger);\n return {\n isCustomAI: false,\n hasAIAccess,\n };\n }\n\n // Package found - now configure it (errors here should propagate, not fall back)\n appLogger([\n colorize('@intlayer/ai', ANSIColors.GREY_LIGHT),\n colorize('found - Run process locally', ANSIColors.GREY_DARK),\n ]);\n\n const aiConfig = await aiClient.getAIConfig({\n userOptions: aiOptions,\n accessType: ['public'],\n });\n\n logAIConfig(aiOptions ?? {}, appLogger);\n\n return {\n aiClient,\n aiConfig,\n isCustomAI: true,\n hasAIAccess: true, // Local AI always has access\n };\n }\n\n // No local AI configured - use backend API\n const hasAIAccess = await checkAIAccess(configuration, aiOptions);\n logAIConfig(aiOptions ?? {}, appLogger);\n\n return {\n isCustomAI: false,\n hasAIAccess,\n };\n};\n"],"mappings":";;;;AAoBA,WAAW,sBAAsB;AAEjC,MAAM,eAAe,WAAsB,cAA6B;AACtE,WAAU;EACR,SAAS,aAAa,WAAW,UAAU;EAC3C,SAAS,WAAW,YAAY,aAAa,WAAW,KAAK;EAC7D,SAAS,YAAY,WAAW,UAAU;EAC1C,SAAS,WAAW,SAAS,aAAa,WAAW,KAAK;EAC1D,SAAS,cAAc,WAAW,UAAU;EAC5C,SAAS,WAAW,SAAS,MAAM,aAAa,WAAW,KAAK;EACjE,CAAC;;;;;;;AAQJ,MAAa,UAAU,OACrB,eACA,cACuC;CACvC,MAAM,YAAY,aAAa,cAAc;AAQ7C,KALE,WAAW,UACX,WAAW,aAAa,YACxB,cAAc,IAAI,UAClB,cAAc,IAAI,aAAa,UAElB;EAEb,IAAIA;AAEJ,MAAI;AACF,cAAW,MAAM,OAAO;UAClB;AAEN,aACE;IACE,SAAS,2CAA2C,WAAW,KAAK;IACpE,SAAS,gBAAgB,WAAW,WAAW;IAC/C,SACE,iFACA,WAAW,KACZ;IACF,EACD,EACE,OAAO,QACR,CACF;GAGD,MAAMC,gBAAc,MAAM,cAAc,eAAe,UAAU;AACjE,eAAY,aAAa,EAAE,EAAE,UAAU;AACvC,UAAO;IACL,YAAY;IACZ;IACD;;AAIH,YAAU,CACR,SAAS,gBAAgB,WAAW,WAAW,EAC/C,SAAS,+BAA+B,WAAW,UAAU,CAC9D,CAAC;EAEF,MAAM,WAAW,MAAM,SAAS,YAAY;GAC1C,aAAa;GACb,YAAY,CAAC,SAAS;GACvB,CAAC;AAEF,cAAY,aAAa,EAAE,EAAE,UAAU;AAEvC,SAAO;GACL;GACA;GACA,YAAY;GACZ,aAAa;GACd;;CAIH,MAAM,cAAc,MAAM,cAAc,eAAe,UAAU;AACjE,aAAY,aAAa,EAAE,EAAE,UAAU;AAEvC,QAAO;EACL,YAAY;EACZ;EACD"}
@@ -0,0 +1,5 @@
1
+ //#region src/ci.d.ts
2
+ declare const runCI: (commands: string[]) => Promise<void>;
3
+ //#endregion
4
+ export { runCI };
5
+ //# sourceMappingURL=ci.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ci.d.ts","names":[],"sources":["../../src/ci.ts"],"sourcesContent":[],"mappings":";cAuBa,+BAAiC"}
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","names":[],"sources":["../../src/cli.ts"],"sourcesContent":[],"mappings":";;;cAmCa;KAwIR,UAAA;EAxIQ,MAAA,CAAA,EAAA,MAEA;EAsIR,OAAA,CAAA,EAAA,OAAU;AAKf,CAAA;AA2Da,KA3DD,oBAAA,GAyoBX;;;;;IApoBG;;;;;;;;;cAsDS,cAAa"}
1
+ {"version":3,"file":"cli.d.ts","names":[],"sources":["../../src/cli.ts"],"sourcesContent":[],"mappings":";;;cAqCa;KAwIR,UAAA;EAxIQ,MAAA,CAAA,EAAA,MAEA;EAsIR,OAAA,CAAA,EAAA,OAAU;AAKf,CAAA;AA2Da,KA3DD,oBAAA,GA2tBX;;;;;IAttBG;;;;;;;;;cAsDS,cAAa"}
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","names":[],"sources":["../../src/config.ts"],"sourcesContent":[],"mappings":";;;KAMK,aAAA;kBACa;AAHQ,CAAA;AAMb,cAAA,SAAuB,EAAA,CAAA,OAAa,CAAA,EAAb,aAAa,EAAA,GAAA,IAAA"}
1
+ {"version":3,"file":"config.d.ts","names":[],"sources":["../../src/config.ts"],"sourcesContent":[],"mappings":";;;KAMK,aAAA;kBACa;AAHQ,CAAA;AAMb,cAAA,SAAuB,EAAA,CAAA,OAAa,CAAb,EAAA,aAAa,EAAA,GAAA,IAAA"}
@@ -3,6 +3,8 @@ import { GetConfigurationOptions } from "@intlayer/config";
3
3
  //#region src/listContentDeclaration.d.ts
4
4
  type ListContentDeclarationOptions = {
5
5
  configOptions?: GetConfigurationOptions;
6
+ json?: boolean;
7
+ absolute?: boolean;
6
8
  };
7
9
  declare const listContentDeclarationRows: (options?: ListContentDeclarationOptions) => {
8
10
  key: string;
@@ -1 +1 @@
1
- {"version":3,"file":"listContentDeclaration.d.ts","names":[],"sources":["../../src/listContentDeclaration.ts"],"sourcesContent":[],"mappings":";;;KAYK,6BAAA;kBACa;AAJQ,CAAA;AAOb,cAAA,0BACD,EAAA,CAAA,OAA6B,CAA7B,EAAA,6BAA6B,EAAA,GAAA;EAe5B,GAAA,EAAA,MAAA;;;cAAA,mCACD"}
1
+ {"version":3,"file":"listContentDeclaration.d.ts","names":[],"sources":["../../src/listContentDeclaration.ts"],"sourcesContent":[],"mappings":";;;KAYK,6BAAA;kBACa;EADb,IAAA,CAAA,EAAA,OAAA;EAMQ,QAAA,CAAA,EAAA,OAAA;AAkBb,CAAA;cAlBa,uCACD;;;;cAiBC,mCACD"}
@@ -0,0 +1,11 @@
1
+ import { ListProjectsOptions } from "@intlayer/chokidar";
2
+
3
+ //#region src/listProjects.d.ts
4
+ type ListProjectsCommandOptions = ListProjectsOptions & {
5
+ json?: boolean;
6
+ absolute?: boolean;
7
+ };
8
+ declare const listProjectsCommand: (options?: ListProjectsCommandOptions) => Promise<void>;
9
+ //#endregion
10
+ export { ListProjectsCommandOptions, listProjectsCommand };
11
+ //# sourceMappingURL=listProjects.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"listProjects.d.ts","names":[],"sources":["../../src/listProjects.ts"],"sourcesContent":[],"mappings":";;;KAIY,0BAAA,GAA6B;;EAA7B,QAAA,CAAA,EAAA,OAAA;AAKZ,CAAA;cAAa,gCACD,+BAA0B"}
@@ -1 +1 @@
1
- {"version":3,"file":"pull.d.ts","names":[],"sources":["../../src/pull.ts"],"sourcesContent":[],"mappings":";;;KAmBK,WAAA;;EAAA,mBAAW,CAAA,EAAA,MAGE;EAcL,aA8OZ,CAAA,EA5PiB,uBAciC;;;;;;cAAtC,iBAAwB,gBAAc"}
1
+ {"version":3,"file":"pull.d.ts","names":[],"sources":["../../src/pull.ts"],"sourcesContent":[],"mappings":";;;KAmBK,WAAA;;EAAA,mBAAW,CAAA,EAAA,MAGE;EAcL,aA8OZ,CAAA,EA5PiB,uBAcwC;;;;;;cAA7C,iBAAwB,gBAAc"}
@@ -6,10 +6,19 @@ import { AIConfig, AIOptions } from "@intlayer/ai";
6
6
 
7
7
  //#region src/translateDoc.d.ts
8
8
 
9
+ /**
10
+ * Shared error state for circuit breaker pattern
11
+ */
12
+ type ErrorState = {
13
+ count: number;
14
+ maxErrors: number;
15
+ shouldStop: boolean;
16
+ };
9
17
  /**
10
18
  * Translate a single file for a given locale
19
+ * Returns TRUE if successful, FALSE if failed/skipped
11
20
  */
12
- declare const translateFile: (baseFilePath: string, outputFilePath: string, locale: Locale, baseLocale: Locale, configuration: IntlayerConfig, aiOptions?: AIOptions, customInstructions?: string, aiClient?: AIClient, aiConfig?: AIConfig) => Promise<void>;
21
+ declare const translateFile: (baseFilePath: string, outputFilePath: string, locale: Locale, baseLocale: Locale, configuration: IntlayerConfig, errorState: ErrorState, aiOptions?: AIOptions, customInstructions?: string, aiClient?: AIClient, aiConfig?: AIConfig) => Promise<boolean>;
13
22
  type TranslateDocOptions = {
14
23
  docPattern: string[];
15
24
  locales: Locale[];
@@ -1 +1 @@
1
- {"version":3,"file":"translateDoc.d.ts","names":[],"sources":["../../src/translateDoc.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;AAmCA;AAGU,cAHG,aAGH,EAAA,CAAA,YAAA,EAAA,MAAA,EAAA,cAAA,EAAA,MAAA,EAAA,MAAA,EAAA,MAAA,EAAA,UAAA,EACI,MADJ,EAAA,aAAA,EAEO,cAFP,EAAA,SAAA,CAAA,EAGI,SAHJ,EAAA,kBAAA,CAAA,EAAA,MAAA,EAAA,QAAA,CAAA,EAKG,QALH,EAAA,QAAA,CAAA,EAMG,QANH,EAAA,GAMW,OANX,CAAA,IAAA,CAAA;KAmIL,mBAAA,GAlIS;EACG,UAAA,EAAA,MAAA,EAAA;EACH,OAAA,EAkIH,MAlIG,EAAA;EAED,mBAAA,EAAA,MAAA,EAAA;EACA,UAAA,EAiIC,MAjID;EAAQ,SAAA,CAAA,EAkIP,SAlIO;EAAA,2BAAA,CAAA,EAAA,MAAA;EA6HhB,aAAA,CAAA,EAOa,uBAPM;EAEb,kBAAA,CAAA,EAAA,MAAA;EAEG,oBAAA,CAAA,EAAA,MAAA,GAAA,MAAA,GAK6B,IAL7B;EACA,mBAAA,CAAA,EAAA,MAAA,GAAA,MAAA,GAK4B,IAL5B;EAEI,YAAA,CAAA,EAAA,OAAA;EAEyB,UAAA,CAAA,EAG5B,mBAH4B;CACD;;;AAS1C;;AAAmC,cAAtB,YAAsB,EAAA,CAAA;EAAA,UAAA;EAAA,OAAA;EAAA,mBAAA;EAAA,UAAA;EAAA,SAAA;EAAA,2BAAA;EAAA,aAAA;EAAA,kBAAA;EAAA,oBAAA;EAAA,mBAAA;EAAA,YAAA;EAAA;AAAA,CAAA,EAahC,mBAbgC,EAAA,GAab,OAba,CAAA,IAAA,CAAA"}
1
+ {"version":3,"file":"translateDoc.d.ts","names":[],"sources":["../../src/translateDoc.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;AA8ByD;AAezD,KAVK,UAAA,GAUQ;EAGH,KAAA,EAAA,MAAA;EACI,SAAA,EAAA,MAAA;EACG,UAAA,EAAA,OAAA;CACH;;;;;AAKJ,cAXG,aAWH,EAAA,CAAA,YAAA,EAAA,MAAA,EAAA,cAAA,EAAA,MAAA,EAAA,MAAA,EARA,MAQA,EAAA,UAAA,EAPI,MAOJ,EAAA,aAAA,EANO,cAMP,EAAA,UAAA,EALI,UAKJ,EAAA,SAAA,CAAA,EAJI,SAIJ,EAAA,kBAAA,CAAA,EAAA,MAAA,EAAA,QAAA,CAAA,EAFG,QAEH,EAAA,QAAA,CAAA,EADG,QACH,EAAA,GAAP,OAAO,CAAA,OAAA,CAAA;AAwKR,KAEG,mBAAA,GAAmB;EAEb,UAAA,EAAA,MAAA,EAAA;EAEG,OAAA,EAFH,MAEG,EAAA;EACA,mBAAA,EAAA,MAAA,EAAA;EAEI,UAAA,EAHJ,MAGI;EAEyB,SAAA,CAAA,EAJ7B,SAI6B;EACD,2BAAA,CAAA,EAAA,MAAA;EAE3B,aAAA,CAAA,EALG,uBAKH;EAAmB,kBAAA,CAAA,EAAA,MAAA;EAOrB,oBAkJZ,CAAA,EAAA,MAAA,GAAA,MAAA,GA5J0C,IA4J1C;EAlJkC,mBAAA,CAAA,EAAA,MAAA,GAAA,MAAA,GATO,IASP;EAAA,YAAA,CAAA,EAAA,OAAA;EAAA,UAAA,CAAA,EAPpB,mBAOoB;CAAA;;;;;AAAA,cAAtB,YAAsB,EAAA,CAAA;EAAA,UAAA;EAAA,OAAA;EAAA,mBAAA;EAAA,UAAA;EAAA,SAAA;EAAA,2BAAA;EAAA,aAAA;EAAA,kBAAA;EAAA,oBAAA;EAAA,mBAAA;EAAA,YAAA;EAAA;AAAA,CAAA,EAahC,mBAbgC,EAAA,GAab,OAba,CAAA,IAAA,CAAA"}
@@ -2,8 +2,8 @@ import { IntlayerConfig } from "@intlayer/types";
2
2
  import { AIOptions } from "@intlayer/api";
3
3
 
4
4
  //#region src/utils/checkAccess.d.ts
5
- declare const checkCMSAuth: (configuration: IntlayerConfig) => Promise<boolean>;
6
- declare const checkAIAccess: (configuration: IntlayerConfig, aiOptions?: AIOptions) => Promise<boolean>;
5
+ declare const checkCMSAuth: (configuration: IntlayerConfig, shouldCheckConfigConsistency?: boolean) => Promise<boolean>;
6
+ declare const checkAIAccess: (configuration: IntlayerConfig, aiOptions?: AIOptions, shouldCheckConfigConsistency?: boolean) => Promise<boolean>;
7
7
  //#endregion
8
8
  export { checkAIAccess, checkCMSAuth };
9
9
  //# sourceMappingURL=checkAccess.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"checkAccess.d.ts","names":[],"sources":["../../../src/utils/checkAccess.ts"],"sourcesContent":[],"mappings":";;;;cAUa,8BACI,mBACd;cAgCU,+BACI,4BACH,cACX"}
1
+ {"version":3,"file":"checkAccess.d.ts","names":[],"sources":["../../../src/utils/checkAccess.ts"],"sourcesContent":[],"mappings":";;;;cAWa,8BACI,2DAEd;cA2EU,+BACI,4BACH,sDAEX"}
@@ -0,0 +1,13 @@
1
+ //#region src/utils/checkConfigConsistency.d.ts
2
+ /**
3
+ * Recursively checks if a subset configuration matches a superset configuration.
4
+ * Throws an error if any value in the subset doesn't match the corresponding value in the superset.
5
+ *
6
+ * @param subset - The subset configuration (e.g., project.configuration from CMS)
7
+ * @param superset - The superset configuration (e.g., local configuration)
8
+ * @throws Error if any value in subset doesn't match the corresponding value in superset
9
+ */
10
+ declare const checkConfigConsistency: (subset: Record<string, any>, superset: Record<string, any>) => void;
11
+ //#endregion
12
+ export { checkConfigConsistency };
13
+ //# sourceMappingURL=checkConfigConsistency.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"checkConfigConsistency.d.ts","names":[],"sources":["../../../src/utils/checkConfigConsistency.ts"],"sourcesContent":[],"mappings":";;AAUA;;;;;;;cAAa,iCACH,+BACE"}
@@ -1 +1 @@
1
- {"version":3,"file":"setupAI.d.ts","names":[],"sources":["../../../src/utils/setupAI.ts"],"sourcesContent":[],"mappings":";;;;;KAUY,QAAA,UAAQ;KAEf,aAAA;aACQ;EAHD,QAAA,CAAA,EAIC,QAJO;EAEf,UAAA,EAAA,OAAa;EA0BL,WAsDZ,EAAA,OAAA;CArDgB;;;;;;cADJ,yBACI,4BACH,cACX,QAAQ"}
1
+ {"version":3,"file":"setupAI.d.ts","names":[],"sources":["../../../src/utils/setupAI.ts"],"sourcesContent":[],"mappings":";;;;;KAUY,QAAA,UAAQ;KAEf,aAAA;aACQ;EAHD,QAAA,CAAA,EAIC,QAJO;EAEf,UAAA,EAAA,OAAa;EA0BL,WAwEZ,EAAA,OAAA;CAvEgB;;;;;;cADJ,yBACI,4BACH,cACX,QAAQ"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intlayer/cli",
3
- "version": "7.5.9",
3
+ "version": "7.5.11",
4
4
  "private": false,
5
5
  "description": "Provides uniform command-line interface scripts for Intlayer, used in packages like intlayer-cli and intlayer.",
6
6
  "keywords": [
@@ -68,14 +68,15 @@
68
68
  },
69
69
  "dependencies": {
70
70
  "@clack/prompts": "^0.11.0",
71
- "@intlayer/api": "7.5.9",
72
- "@intlayer/chokidar": "7.5.9",
73
- "@intlayer/config": "7.5.9",
74
- "@intlayer/core": "7.5.9",
75
- "@intlayer/dictionaries-entry": "7.5.9",
76
- "@intlayer/remote-dictionaries-entry": "7.5.9",
77
- "@intlayer/types": "7.5.9",
78
- "@intlayer/unmerged-dictionaries-entry": "7.5.9",
71
+ "@intlayer/ai": "7.5.11",
72
+ "@intlayer/api": "7.5.11",
73
+ "@intlayer/chokidar": "7.5.11",
74
+ "@intlayer/config": "7.5.11",
75
+ "@intlayer/core": "7.5.11",
76
+ "@intlayer/dictionaries-entry": "7.5.11",
77
+ "@intlayer/remote-dictionaries-entry": "7.5.11",
78
+ "@intlayer/types": "7.5.11",
79
+ "@intlayer/unmerged-dictionaries-entry": "7.5.11",
79
80
  "commander": "14.0.1",
80
81
  "eventsource": "3.0.7",
81
82
  "fast-glob": "3.3.3"
@@ -91,7 +92,7 @@
91
92
  "vitest": "4.0.16"
92
93
  },
93
94
  "peerDependencies": {
94
- "@intlayer/ai": "7.5.9"
95
+ "@intlayer/ai": "7.5.11"
95
96
  },
96
97
  "peerDependenciesMeta": {
97
98
  "@intlayer/ai": {