aiex-cli 0.0.3 → 0.0.4-beta.1

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 (117) hide show
  1. package/dist/cli.mjs +346 -251
  2. package/dist/{doctor-collector-BVbuw5LY.mjs → doctor-collector-xRnW5Rj3.mjs} +541 -8
  3. package/dist/index.mjs +1 -1
  4. package/dist/web/assets/AISettings-BmCr8Kj4.js +272 -0
  5. package/dist/web/assets/DataBrowser-IlgTMGi0.js +97 -0
  6. package/dist/web/assets/ExtractionViewer-0F4C26V5.js +1 -0
  7. package/dist/web/assets/JsonSchemaEditor-Dyl391lX.js +571 -0
  8. package/dist/web/assets/{api-client-BsgtGnzl.js → api-client-b4ZBXpNH.js} +1 -1
  9. package/dist/web/assets/{cssMode-CPThwItX.js → cssMode-BM5FOYIl.js} +1 -1
  10. package/dist/web/assets/dialog-CnZ7jH1l.js +109 -0
  11. package/dist/web/assets/dist-CElVIpns.js +1 -0
  12. package/dist/web/assets/{editor.main-BnOkwRFv.js → editor.main-C2Q97Dkk.js} +2 -2
  13. package/dist/web/assets/{freemarker2-DWDTYVJR.js → freemarker2-BqyJTCTn.js} +1 -1
  14. package/dist/web/assets/{handlebars-D4DzjGQ7.js → handlebars-DxRJTefg.js} +1 -1
  15. package/dist/web/assets/{html-DnzhKSoD.js → html-gyvgrapw.js} +1 -1
  16. package/dist/web/assets/{htmlMode-CR7UKfEH.js → htmlMode-CNjCRwdY.js} +1 -1
  17. package/dist/web/assets/{index-CPjJbU4i.js → index-CGZLSwt2.js} +38 -38
  18. package/dist/web/assets/{javascript-D2srszZ8.js → javascript-BK6ufvq6.js} +1 -1
  19. package/dist/web/assets/{jsonMode-B4jaPYEr.js → jsonMode-m2trGjkO.js} +1 -1
  20. package/dist/web/assets/{liquid-CIT2Wl_l.js → liquid-BtyuYqQQ.js} +1 -1
  21. package/dist/web/assets/{mdx-CWLaEOFy.js → mdx-C8K4EvCQ.js} +1 -1
  22. package/dist/web/assets/{monaco.contribution-DDv5ldfS.js → monaco.contribution-BTr-G8hO.js} +2 -2
  23. package/dist/web/assets/object-utils-C6FkG7fw.js +1 -0
  24. package/dist/web/assets/{python-6CGfpCNq.js → python-8dyH1nS_.js} +1 -1
  25. package/dist/web/assets/{razor-DEMMh3TD.js → razor-DtWMI74k.js} +1 -1
  26. package/dist/web/assets/textarea-DMpqBhjw.js +522 -0
  27. package/dist/web/assets/{tsMode-Cm1NtjPs.js → tsMode-Dv8YG-YK.js} +1 -1
  28. package/dist/web/assets/{typescript-BM9aPEFg.js → typescript-DbClKYS3.js} +1 -1
  29. package/dist/web/assets/vue-i18n-Du42D0vb.js +931 -0
  30. package/dist/web/assets/{xml-CoSbvcg5.js → xml-Bb59gjP6.js} +1 -1
  31. package/dist/web/assets/{yaml-56GOgy8k.js → yaml-DVMb_IfV.js} +1 -1
  32. package/dist/web/index.html +8 -8
  33. package/dist/zh-CN-DAlmQ2hb.mjs +484 -0
  34. package/package.json +3 -1
  35. package/dist/web/assets/AISettings-D6EpB8tt.js +0 -272
  36. package/dist/web/assets/DataBrowser-N77fBaoa.js +0 -97
  37. package/dist/web/assets/ExtractionViewer-BSZycwgL.js +0 -1
  38. package/dist/web/assets/JsonSchemaEditor-DfHs5bc0.js +0 -571
  39. package/dist/web/assets/button-Cdgr9Igy.js +0 -927
  40. package/dist/web/assets/dialog-CUkPLPNP.js +0 -109
  41. package/dist/web/assets/dist-9yHVMqQ0.js +0 -1
  42. package/dist/web/assets/object-utils-I4gWdSnS.js +0 -1
  43. package/dist/web/assets/runtime-dom.esm-bundler-ei_N7Xjw.js +0 -1
  44. package/dist/web/assets/textarea-DEQMRfG8.js +0 -522
  45. /package/dist/{completions-C3rmTwXZ.mjs → completions-Bh0DOngr.mjs} +0 -0
  46. /package/dist/web/assets/{abap-Bgec7Keq.js → abap-DiwvWnMr.js} +0 -0
  47. /package/dist/web/assets/{apex-VBlPwEoQ.js → apex-CmtZjKlf.js} +0 -0
  48. /package/dist/web/assets/{azcli-DKqrEFBx.js → azcli-DL2My_i-.js} +0 -0
  49. /package/dist/web/assets/{bat-DdgQWy_0.js → bat-B-nC98wG.js} +0 -0
  50. /package/dist/web/assets/{bicep-CRMM43EB.js → bicep-Ju5MwOgh.js} +0 -0
  51. /package/dist/web/assets/{cameligo-UatALtML.js → cameligo-8Eu1TyBr.js} +0 -0
  52. /package/dist/web/assets/{clojure-D8JU08RA.js → clojure-u-RpMkH3.js} +0 -0
  53. /package/dist/web/assets/{coffee-C56wu358.js → coffee-CdA7bbTe.js} +0 -0
  54. /package/dist/web/assets/{cpp-CyZLvhJG.js → cpp-CzNFP8ks.js} +0 -0
  55. /package/dist/web/assets/{csharp-BJl3ixva.js → csharp-j1LThmcE.js} +0 -0
  56. /package/dist/web/assets/{csp-CxEKxmO-.js → csp-CLRC61y6.js} +0 -0
  57. /package/dist/web/assets/{css-B0t_muXd.js → css-r6rC_7P2.js} +0 -0
  58. /package/dist/web/assets/{cypher-D1hqiMFD.js → cypher-CW08XVUh.js} +0 -0
  59. /package/dist/web/assets/{dart-Bz550Pyv.js → dart-Cs9aL5T_.js} +0 -0
  60. /package/dist/web/assets/{dockerfile-CIXgVAuA.js → dockerfile-BWM0M184.js} +0 -0
  61. /package/dist/web/assets/{ecl-D9qbvZoA.js → ecl-MJJuer5P.js} +0 -0
  62. /package/dist/web/assets/{editor.api-C8BHpRhn.js → editor.api-nsOUOZde.js} +0 -0
  63. /package/dist/web/assets/{elixir-b2M38fAy.js → elixir-D2AIuXqn.js} +0 -0
  64. /package/dist/web/assets/{flow9-Dq1UYMkt.js → flow9-B2H24giC.js} +0 -0
  65. /package/dist/web/assets/{fsharp-BaeLhgfq.js → fsharp-CFNadkg7.js} +0 -0
  66. /package/dist/web/assets/{go-Bd-NFKIC.js → go-dSur1iB2.js} +0 -0
  67. /package/dist/web/assets/{graphql-DZVerJfy.js → graphql-qyhAo11d.js} +0 -0
  68. /package/dist/web/assets/{hcl-CAVzrZfH.js → hcl-DFzjMyzm.js} +0 -0
  69. /package/dist/web/assets/{ini-CyXdX58t.js → ini-TdzA8TIl.js} +0 -0
  70. /package/dist/web/assets/{java-B5pNgvhy.js → java-CSGA9pkE.js} +0 -0
  71. /package/dist/web/assets/{julia-XRhmV3AN.js → julia-9izz5OsY.js} +0 -0
  72. /package/dist/web/assets/{kotlin-DOd3J5vr.js → kotlin-DuPK7AtF.js} +0 -0
  73. /package/dist/web/assets/{less-veZSnyw6.js → less-B8d93iCg.js} +0 -0
  74. /package/dist/web/assets/{lexon-QWGkuK0H.js → lexon-DWtEIyu7.js} +0 -0
  75. /package/dist/web/assets/{lua-CYGpjuO5.js → lua-Ciq0OGgt.js} +0 -0
  76. /package/dist/web/assets/{m3-yNnrZkdc.js → m3-Cki6JWj_.js} +0 -0
  77. /package/dist/web/assets/{markdown-BCSWEPSX.js → markdown-Cu47xwU0.js} +0 -0
  78. /package/dist/web/assets/{mips-OpYmcC30.js → mips-BM8ui995.js} +0 -0
  79. /package/dist/web/assets/{msdax-2oxoTO9Z.js → msdax-DqLio0_c.js} +0 -0
  80. /package/dist/web/assets/{mysql-5KlC-K_9.js → mysql-v1wbjJOq.js} +0 -0
  81. /package/dist/web/assets/{objective-c-CcDCgtLx.js → objective-c-CQl3PGSB.js} +0 -0
  82. /package/dist/web/assets/{pascal-BZGsbaEV.js → pascal-D4iW0ZtD.js} +0 -0
  83. /package/dist/web/assets/{pascaligo-DtD5qU3G.js → pascaligo-BdC9CZdj.js} +0 -0
  84. /package/dist/web/assets/{perl-C1jNNS3E.js → perl-BL10m4XD.js} +0 -0
  85. /package/dist/web/assets/{pgsql-CT0fhiZa.js → pgsql-Be_oqVo3.js} +0 -0
  86. /package/dist/web/assets/{php-D6DrXoPM.js → php-BtvXSFRI.js} +0 -0
  87. /package/dist/web/assets/{pla-b3-HN2pF.js → pla-B2vUy15C.js} +0 -0
  88. /package/dist/web/assets/{postiats-Bin2ApVS.js → postiats-CbmTTfXr.js} +0 -0
  89. /package/dist/web/assets/{powerquery-7ASnn-ZG.js → powerquery-DszLhJGx.js} +0 -0
  90. /package/dist/web/assets/{powershell-t4p7sU1H.js → powershell-B0dYktF6.js} +0 -0
  91. /package/dist/web/assets/{preload-helper-Dd-HcVz_.js → preload-helper-DWTEM3RW.js} +0 -0
  92. /package/dist/web/assets/{protobuf-BUGeWa_j.js → protobuf-CZvaj1VX.js} +0 -0
  93. /package/dist/web/assets/{pug-BuKcgC9s.js → pug-CPDx1B3S.js} +0 -0
  94. /package/dist/web/assets/{qsharp-DxLLX8mo.js → qsharp-CAxMZVjw.js} +0 -0
  95. /package/dist/web/assets/{r-DMlFgn7A.js → r-8DbbFX2l.js} +0 -0
  96. /package/dist/web/assets/{redis-cXItkC5u.js → redis-DRWj9MtJ.js} +0 -0
  97. /package/dist/web/assets/{redshift-BZVbW7HE.js → redshift-C6cElE_5.js} +0 -0
  98. /package/dist/web/assets/{restructuredtext-BzjxwS8h.js → restructuredtext-W9pS9n3m.js} +0 -0
  99. /package/dist/web/assets/{ruby-C5nyLV4l.js → ruby-BKnzWnk-.js} +0 -0
  100. /package/dist/web/assets/{rust-BcmMsHdf.js → rust-YPCclWwe.js} +0 -0
  101. /package/dist/web/assets/{sb-Dnb1iy6B.js → sb-BgM4DTFb.js} +0 -0
  102. /package/dist/web/assets/{scala-anMIFYpA.js → scala-fz1OPLMl.js} +0 -0
  103. /package/dist/web/assets/{scheme-BItQTe08.js → scheme-8Uz1RIbu.js} +0 -0
  104. /package/dist/web/assets/{scss-BOv51BJ5.js → scss-Djo3IYXr.js} +0 -0
  105. /package/dist/web/assets/{shell-BsRYRTNN.js → shell-CINF5Tx_.js} +0 -0
  106. /package/dist/web/assets/{solidity-BtuLgGDx.js → solidity-GgiNEuUm.js} +0 -0
  107. /package/dist/web/assets/{sophia-B0Vkc5MF.js → sophia-Culj97P9.js} +0 -0
  108. /package/dist/web/assets/{sparql-B7lvkZQM.js → sparql-C2ZlpxOY.js} +0 -0
  109. /package/dist/web/assets/{sql-DvP5MpA3.js → sql-BEf5Pg7Y.js} +0 -0
  110. /package/dist/web/assets/{st-GVUeyB3U.js → st-CT6UUoeH.js} +0 -0
  111. /package/dist/web/assets/{swift-DSPIoCjm.js → swift-B5g0xTG3.js} +0 -0
  112. /package/dist/web/assets/{systemverilog-Icj2-k23.js → systemverilog-CEgQz9DR.js} +0 -0
  113. /package/dist/web/assets/{tcl-Cd8KQcm-.js → tcl-D0qL2L0I.js} +0 -0
  114. /package/dist/web/assets/{twig-CBHmt8z3.js → twig-BFUAVf1E.js} +0 -0
  115. /package/dist/web/assets/{typespec-Ckc037mq.js → typespec-CjVVcNKm.js} +0 -0
  116. /package/dist/web/assets/{vb-B97GW9Wb.js → vb-CZJr-DQz.js} +0 -0
  117. /package/dist/web/assets/{wgsl-DIKmb3YH.js → wgsl-ivoXUo2e.js} +0 -0
package/dist/cli.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { C as name, D as doctorDiagnosticsTableRows, O as formatDoctorDiagnosticsJson, S as description, T as version, _ as PLACEHOLDER_SCHEMA, a as parseJsonSchema, b as createConfig, c as recognizeImageText, d as readAIConfig, f as writeAIConfig, g as DEFAULT_PROMPT_CONFIG, h as DEFAULT_MINERU_CONFIG, i as JsonSchemaDefinitionSchema, l as shouldUseImageOcrFallback, m as DEFAULT_MARKITDOWN_CONFIG, n as createMigrationConfig, o as toSnakeCase, p as DEFAULT_MARKER_CONFIG, s as generateDrizzleSchema, t as collectDoctorDiagnostics, u as getDefaultAIConfig, v as PLACEHOLDER_TEXT, w as package_default, x as seedConfig, y as AIConfigSchema } from "./doctor-collector-BVbuw5LY.mjs";
1
+ import { A as formatDoctorDiagnosticsJson, C as seedConfig, D as version, E as package_default, S as createConfig, T as name, _ as DEFAULT_MINERU_CONFIG, a as parseJsonSchema, b as PLACEHOLDER_TEXT, c as recognizeImageText, d as t, f as getDefaultAIConfig, g as DEFAULT_MARKITDOWN_CONFIG, h as DEFAULT_MARKER_CONFIG, i as JsonSchemaDefinitionSchema, k as doctorDiagnosticsTableRows, l as shouldUseImageOcrFallback, m as writeAIConfig, n as createMigrationConfig, o as toSnakeCase, p as readAIConfig, s as generateDrizzleSchema, t as collectDoctorDiagnostics, u as initI18n, v as DEFAULT_PROMPT_CONFIG, w as description, x as AIConfigSchema, y as PLACEHOLDER_SCHEMA } from "./doctor-collector-xRnW5Rj3.mjs";
2
2
  import { createRequire } from "node:module";
3
3
  import fs from "node:fs/promises";
4
4
  import os from "node:os";
@@ -156,11 +156,11 @@ function generateCompletionScript(name$1, shell) {
156
156
  const completionCommand = defineCommand({
157
157
  meta: {
158
158
  name: "completion",
159
- description: "Generate shell completion scripts (bash|zsh|fish)\n\nUsage:\n aiex completion bash # source <(aiex completion bash)\n aiex completion zsh # source <(aiex completion zsh)\n aiex completion fish # aiex completion fish | source"
159
+ description: t("command.completion.description")
160
160
  },
161
161
  args: { shell: {
162
162
  type: "positional",
163
- description: "Shell type: bash, zsh, fish",
163
+ description: t("command.completion.args.shell"),
164
164
  required: true
165
165
  } },
166
166
  async run({ args }) {
@@ -169,7 +169,7 @@ const completionCommand = defineCommand({
169
169
  try {
170
170
  process.stdout.write(generateCompletionScript(name$1, shell));
171
171
  } catch (error) {
172
- process.stderr.write(`Error: ${error instanceof Error ? error.message : String(error)}\n`);
172
+ process.stderr.write(`${t("command.completion.error", { error: error instanceof Error ? error.message : String(error) })}\n`);
173
173
  process.exit(1);
174
174
  }
175
175
  }
@@ -180,11 +180,11 @@ const completionCommand = defineCommand({
180
180
  const doctorCommand = defineCommand({
181
181
  meta: {
182
182
  name: "doctor",
183
- description: "Print environment and configuration diagnostics"
183
+ description: t("command.doctor.description")
184
184
  },
185
185
  args: { json: {
186
186
  type: "boolean",
187
- description: "Print diagnostics as JSON"
187
+ description: t("command.doctor.args.json")
188
188
  } },
189
189
  async run({ args }) {
190
190
  try {
@@ -194,15 +194,15 @@ const doctorCommand = defineCommand({
194
194
  return;
195
195
  }
196
196
  consola.info(`${diagnostics.cli.name} ${diagnostics.cli.version}`);
197
- const t = new CliTable3({
198
- head: ["key", "value"],
197
+ const table = new CliTable3({
198
+ head: [t("command.doctor.headers.0"), t("command.doctor.headers.1")],
199
199
  colAligns: ["right", "left"],
200
200
  style: { compact: true }
201
201
  });
202
- t.push(...doctorDiagnosticsTableRows(diagnostics));
203
- process.stdout.write(`${t.toString()}\n`);
202
+ table.push(...doctorDiagnosticsTableRows(diagnostics));
203
+ process.stdout.write(`${table.toString()}\n`);
204
204
  } catch (err) {
205
- consola.error(`Doctor diagnostics failed: ${err}`);
205
+ consola.error(t("command.doctor.diagnosticsFailed", { error: err }));
206
206
  }
207
207
  }
208
208
  });
@@ -211,7 +211,7 @@ const doctorCommand = defineCommand({
211
211
  //#region src/commands/utils.ts
212
212
  function failCommand(message) {
213
213
  if (message) consola.error(message);
214
- outro("Failed!");
214
+ outro(t("common.failed"));
215
215
  process.exitCode = 1;
216
216
  }
217
217
 
@@ -12875,15 +12875,18 @@ function filterCompatible(models, inputTokens, outputTokens) {
12875
12875
  }
12876
12876
  function selectModel(input) {
12877
12877
  const { models, isImage, fileName, inputTokens, outputTokens } = input;
12878
- if (models.length === 0) throw new Error("No AI models configured. Please add at least one model in AI Settings.");
12878
+ if (models.length === 0) throw new Error(t("errors.ai.noModels"));
12879
12879
  let candidates = filterCompatible(models, inputTokens, outputTokens);
12880
12880
  if (candidates.length === 0) candidates = models;
12881
12881
  if (isImage) {
12882
12882
  const visionModel = candidates.find((m) => m.capabilities.vision);
12883
12883
  if (!visionModel) {
12884
12884
  const hint = fileName ? ` (${fileName})` : "";
12885
- const msg = inputTokens ? `No vision-capable model with sufficient context window (≥${inputTokens} tokens) found${hint}.` : `Image input requires a model with vision capability${hint}.`;
12886
- throw new Error(`${msg} Please add a suitable vision-capable model in AI Settings.`);
12885
+ const msg = inputTokens ? t("errors.ai.noVisionModelContext", {
12886
+ tokens: inputTokens,
12887
+ hint
12888
+ }) : t("errors.ai.noVisionModel", { hint });
12889
+ throw new Error(msg + t("errors.ai.addSuitableModel"));
12887
12890
  }
12888
12891
  return {
12889
12892
  name: visionModel.name,
@@ -13140,7 +13143,7 @@ async function extractStructuredData(input) {
13140
13143
  const { config, schema, text: text$1, aiexDir, file, modelOverride } = input;
13141
13144
  if (!config.provider.apiKey) return {
13142
13145
  success: false,
13143
- error: "API Key not configured. Please configure AI settings in the web UI."
13146
+ error: t("errors.ai.apiKeyMissing")
13144
13147
  };
13145
13148
  const useFileContent = !!file;
13146
13149
  const isImageFile = useFileContent && detectMimeType(file).startsWith("image/");
@@ -13345,7 +13348,7 @@ function insertExtractedData(db, schema, data) {
13345
13348
  const [propName] = propEntry;
13346
13349
  const nestedValue = data[propName];
13347
13350
  if (nestedValue === null || nestedValue === void 0) continue;
13348
- const nestedTable = parseResult.tables.find((t) => t.name === revRel.toTable);
13351
+ const nestedTable = parseResult.tables.find((t$1) => t$1.name === revRel.toTable);
13349
13352
  if (!nestedTable) continue;
13350
13353
  if (revRel.type === "has-one") {
13351
13354
  const rowId = insertTableRow({
@@ -13440,7 +13443,7 @@ async function createExtractionAuditRecord(aiexDir, input) {
13440
13443
  }
13441
13444
  async function updateExtractionAuditRecord(aiexDir, id, patch) {
13442
13445
  const current = await readExtractionAuditRecord(aiexDir, id);
13443
- if (!current) throw new Error(`Extraction audit record not found: ${id}`);
13446
+ if (!current) throw new Error(t("errors.extractionAudit.recordNotFound", { id }));
13444
13447
  const record = {
13445
13448
  ...current,
13446
13449
  ...patch,
@@ -13473,7 +13476,7 @@ async function markStaleIfNeeded(aiexDir, record) {
13473
13476
  const staleRecord = {
13474
13477
  ...record,
13475
13478
  status: "stale",
13476
- error: record.error ?? "Extraction did not finish. It may have been interrupted.",
13479
+ error: record.error ?? t("errors.extractionAudit.interrupted"),
13477
13480
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
13478
13481
  };
13479
13482
  await writeFile(auditPath(aiexDir, staleRecord.id), staleRecord, {
@@ -13543,7 +13546,7 @@ async function findSucceededAuditByHash(aiexDir, schemaName, fileHash) {
13543
13546
  const MAX_UPLOAD_SIZE = 30 * 1024 * 1024;
13544
13547
  const MAX_UPLOAD_SIZE_TEXT = "30MB";
13545
13548
  const SUPPORTED_FILE_TYPES_TEXT = "images, PDF, text, markdown, CSV, JSON, HTML, XML, YAML";
13546
- const MISSING_UPLOAD_FILE_TEXT = "Uploaded file is no longer available. Re-run extraction with the original file.";
13549
+ const MISSING_UPLOAD_FILE_TEXT = t("errors.file.missingUpload");
13547
13550
  const SUPPORTED_MIME_TYPES = new Set([
13548
13551
  "image/png",
13549
13552
  "image/jpeg",
@@ -13588,7 +13591,10 @@ function isAllowedMimeType(mimeType) {
13588
13591
  return SUPPORTED_MIME_TYPES.has(mimeType);
13589
13592
  }
13590
13593
  function unsupportedFileTypeMessage(mimeType) {
13591
- return `Unsupported file type "${mimeType}". Supported: ${SUPPORTED_FILE_TYPES_TEXT}.`;
13594
+ return t("errors.file.unsupportedType", {
13595
+ type: mimeType,
13596
+ supported: SUPPORTED_FILE_TYPES_TEXT
13597
+ });
13592
13598
  }
13593
13599
  function isMissingUploadFileError(error) {
13594
13600
  return !!error && typeof error === "object" && error.code === "ENOENT";
@@ -13600,8 +13606,12 @@ var FileValidationError = class extends Error {
13600
13606
  }
13601
13607
  };
13602
13608
  function validateFileUpload(file) {
13603
- if (file.size === 0) throw new FileValidationError("Uploaded file is empty");
13604
- if (file.size > MAX_UPLOAD_SIZE) throw new FileValidationError(`File size (${bytesToMB(file.size).toFixed(1)}MB) exceeds ${MAX_UPLOAD_SIZE_TEXT} limit`);
13609
+ if (file.size === 0) throw new FileValidationError(t("errors.file.empty"));
13610
+ if (file.size > MAX_UPLOAD_SIZE) throw new FileValidationError(t("errors.file.sizeExceeded", {
13611
+ size: bytesToMB(file.size).toFixed(1),
13612
+ limit: MAX_UPLOAD_SIZE_TEXT,
13613
+ file: file.name
13614
+ }));
13605
13615
  if (!isAllowedMimeType(file.type)) throw new FileValidationError(unsupportedFileTypeMessage(file.type));
13606
13616
  }
13607
13617
 
@@ -13756,7 +13766,7 @@ function firstDataSourceId(database) {
13756
13766
  }
13757
13767
  async function resolveNotionDataSource(notion, inputId) {
13758
13768
  const id = parseNotionDatabaseId(inputId);
13759
- if (!id) throw new Error("Notion database or data source URL/ID is required.");
13769
+ if (!id) throw new Error(t("errors.notion.idRequired"));
13760
13770
  try {
13761
13771
  const dataSource$1 = await notion.dataSources.retrieve({ data_source_id: id });
13762
13772
  if (isDataSourceResponse(dataSource$1)) return {
@@ -13768,9 +13778,9 @@ async function resolveNotionDataSource(notion, inputId) {
13768
13778
  } catch {}
13769
13779
  const database = await notion.databases.retrieve({ database_id: id });
13770
13780
  const dataSourceId = firstDataSourceId(database);
13771
- if (!dataSourceId) throw new Error("No data source found for this Notion database. Copy the data source link from Notion, or share the source database with the integration.");
13781
+ if (!dataSourceId) throw new Error(t("errors.notion.noDataSource"));
13772
13782
  const dataSource = await notion.dataSources.retrieve({ data_source_id: dataSourceId });
13773
- if (!isDataSourceResponse(dataSource)) throw new Error("Notion data source did not return properties. Make sure it is shared with the integration and is not a linked data source.");
13783
+ if (!isDataSourceResponse(dataSource)) throw new Error(t("errors.notion.noProperties"));
13774
13784
  return {
13775
13785
  databaseId: database.id,
13776
13786
  dataSourceId: dataSource.id,
@@ -13779,9 +13789,9 @@ async function resolveNotionDataSource(notion, inputId) {
13779
13789
  };
13780
13790
  }
13781
13791
  async function inspectNotionDatabase(input) {
13782
- if (!input.token.trim()) throw new Error("Notion integration token is required.");
13792
+ if (!input.token.trim()) throw new Error(t("errors.notion.tokenRequired"));
13783
13793
  const id = parseNotionDatabaseId(input.databaseId);
13784
- if (!id) throw new Error("Notion database or data source URL/ID is required.");
13794
+ if (!id) throw new Error(t("errors.notion.idRequired"));
13785
13795
  const resolved = await resolveNotionDataSource(new Client({ auth: input.token }), id);
13786
13796
  const databaseProperties = resolved.properties;
13787
13797
  const titleProperty = findTitleProperty(databaseProperties) ?? void 0;
@@ -13797,8 +13807,8 @@ async function inspectNotionDatabase(input) {
13797
13807
  };
13798
13808
  }
13799
13809
  function validateNotionConfig(config) {
13800
- if (!config?.enabled) return "Notion export is not enabled. Configure Notion settings first.";
13801
- if (!config.token.trim()) return "Notion integration token is required.";
13810
+ if (!config?.enabled) return t("errors.notion.notEnabled");
13811
+ if (!config.token.trim()) return t("errors.notion.tokenRequired");
13802
13812
  return null;
13803
13813
  }
13804
13814
  async function writeNotionPage(config, schemaName, data) {
@@ -13806,8 +13816,8 @@ async function writeNotionPage(config, schemaName, data) {
13806
13816
  if (configError) throw new Error(configError);
13807
13817
  const notionConfig = config;
13808
13818
  const schemaConfig = notionConfig.schemas[schemaName];
13809
- if (!schemaConfig) throw new Error(`Notion database is not configured for schema "${schemaName}".`);
13810
- if (!schemaConfig.databaseId.trim()) throw new Error(`Notion database ID is required for schema "${schemaName}".`);
13819
+ if (!schemaConfig) throw new Error(t("errors.notion.noSchemaConfig", { name: schemaName }));
13820
+ if (!schemaConfig.databaseId.trim()) throw new Error(t("errors.notion.noDatabaseId", { name: schemaName }));
13811
13821
  const notion = new Client({ auth: notionConfig.token });
13812
13822
  const resolved = await resolveNotionDataSource(notion, schemaConfig.databaseId);
13813
13823
  const databaseProperties = resolved.properties;
@@ -13825,7 +13835,7 @@ async function writeNotionPage(config, schemaName, data) {
13825
13835
  }
13826
13836
  const titleProperty = findTitleProperty(databaseProperties, schemaConfig.titleProperty);
13827
13837
  if (titleProperty && !properties[titleProperty]) properties[titleProperty] = buildPropertyValue("title", schemaName);
13828
- if (Object.keys(properties).length === 0) throw new Error("No extracted fields matched Notion database properties.");
13838
+ if (Object.keys(properties).length === 0) throw new Error(t("errors.notion.noFieldsMatched"));
13829
13839
  return {
13830
13840
  pageId: (await notion.pages.create({
13831
13841
  parent: resolved.parent,
@@ -13962,7 +13972,10 @@ var FallbackPdfConverter = class {
13962
13972
  try {
13963
13973
  return await this.primary.convert(input, filePath);
13964
13974
  } catch (err) {
13965
- consola.warn(`${this.primary.name} failed: ${err instanceof Error ? err.message : String(err)}`);
13975
+ consola.warn(t("command.extract.file.errorProcessing", {
13976
+ name: this.primary.name,
13977
+ error: err instanceof Error ? err.message : String(err)
13978
+ }));
13966
13979
  consola.info(`Falling back to ${this.fallback.name}`);
13967
13980
  const result = await this.fallback.convert(input, filePath);
13968
13981
  return {
@@ -13994,14 +14007,14 @@ function createPdfConverter(config) {
13994
14007
  return withFallback(new ExternalCommandPdfConverter("marker", markerConfig), markerConfig);
13995
14008
  }
13996
14009
  if (config.converter === "external") {
13997
- if (!config.external) throw new Error("External PDF converter is selected but no external command is configured.");
14010
+ if (!config.external) throw new Error(t("errors.pdf.externalNotConfigured"));
13998
14011
  return withFallback(new ExternalCommandPdfConverter("external", config.external), config.external);
13999
14012
  }
14000
14013
  }
14001
14014
  const key = typeof config === "string" ? config : "unpdf";
14002
14015
  let instance = registry.get(key);
14003
14016
  if (!instance) {
14004
- if (key !== "unpdf") throw new Error(`PDF converter "${key}" requires configuration.`);
14017
+ if (key !== "unpdf") throw new Error(t("errors.pdf.converterRequiresConfig", { name: key }));
14005
14018
  instance = new UnpdfConverter();
14006
14019
  registry.set(key, instance);
14007
14020
  }
@@ -14050,7 +14063,7 @@ const PDF_EXT_RE = /\.pdf$/i;
14050
14063
  const JSON_EXT_RE$1 = /\.json$/;
14051
14064
  const SUPPORTED_FILE_PATTERN = `*.{${[...SUPPORTED_EXTENSIONS$1].join(",")}}`;
14052
14065
  async function syncResultToNotion(aiConfig, schemaName, data) {
14053
- if (!data || typeof data !== "object" || Array.isArray(data)) throw new Error("Extraction result is not an object and cannot be written to Notion.");
14066
+ if (!data || typeof data !== "object" || Array.isArray(data)) throw new Error(t("errors.ai.extractionNotObject"));
14054
14067
  const page = await writeNotionPage(aiConfig.notion, schemaName, data);
14055
14068
  return [{
14056
14069
  databaseId: page.databaseId,
@@ -14064,23 +14077,29 @@ async function ensureDatabaseReady(dbPath, schema) {
14064
14077
  try {
14065
14078
  await fs.access(dbPath);
14066
14079
  } catch {
14067
- return `Database not found at ${pc.cyan(".aiex/database.db")}. Run ${pc.cyan("aiex schema")} first to create the database.`;
14080
+ return t("errors.db.notFound", {
14081
+ path: pc.cyan(".aiex/database.db"),
14082
+ cmd: pc.cyan("aiex schema")
14083
+ });
14068
14084
  }
14069
14085
  try {
14070
14086
  const result = parseJsonSchema(schema);
14071
14087
  const db = new Database(dbPath);
14072
14088
  try {
14073
- for (const table of result.tables) if (!db.prepare(`SELECT name FROM sqlite_master WHERE type='table' AND name=?`).get(table.name)) return `Table "${table.name}" not found in database. Run ${pc.cyan("aiex schema")} first to create tables.`;
14089
+ for (const table of result.tables) if (!db.prepare(`SELECT name FROM sqlite_master WHERE type='table' AND name=?`).get(table.name)) return t("errors.db.tableNotFound", {
14090
+ name: table.name,
14091
+ cmd: pc.cyan("aiex schema")
14092
+ });
14074
14093
  } finally {
14075
14094
  db.close();
14076
14095
  }
14077
14096
  } catch (e) {
14078
- return `Cannot verify database: ${e instanceof Error ? e.message : String(e)}`;
14097
+ return t("errors.db.cannotVerify", { error: e instanceof Error ? e.message : String(e) });
14079
14098
  }
14080
14099
  return null;
14081
14100
  }
14082
14101
  function listSupportedFiles(dir, pattern) {
14083
- if (!fs$1.statSync(dir).isDirectory()) throw new Error(`Not a directory: ${dir}`);
14102
+ if (!fs$1.statSync(dir).isDirectory()) throw new Error(t("errors.file.notADirectory", { dir }));
14084
14103
  return globSync(pattern ?? SUPPORTED_FILE_PATTERN, {
14085
14104
  cwd: dir,
14086
14105
  absolute: true,
@@ -14098,15 +14117,18 @@ async function loadSchema(config, schemaName) {
14098
14117
  } catch (e) {
14099
14118
  if (e instanceof ZodError) return {
14100
14119
  schema: null,
14101
- error: `Schema validation failed: ${schemaName}.json\n${e.issues.map((i) => ` - ${i.path.join(".")}: ${i.message}`).join("\n")}`
14120
+ error: t("errors.schema.validationFailed", {
14121
+ name: `${schemaName}.json`,
14122
+ issues: e.issues.map((i) => ` - ${i.path.join(".")}: ${i.message}`).join("\n")
14123
+ })
14102
14124
  };
14103
14125
  if (e.code === "ENOENT") return {
14104
14126
  schema: null,
14105
- error: `Cannot read schema file: ${schemaName}.json`
14127
+ error: t("errors.schema.cannotRead", { name: `${schemaName}.json` })
14106
14128
  };
14107
14129
  if (e instanceof SyntaxError) return {
14108
14130
  schema: null,
14109
- error: `Invalid JSON in schema file: ${schemaName}.json`
14131
+ error: t("errors.schema.invalidJson", { name: `${schemaName}.json` })
14110
14132
  };
14111
14133
  return {
14112
14134
  schema: null,
@@ -14124,12 +14146,16 @@ async function listSchemas(aiexDir) {
14124
14146
  }
14125
14147
  async function readExtractFileInput(filePath, aiConfig, modelOverride) {
14126
14148
  const stat = fs$1.statSync(filePath);
14127
- if (stat.size > MAX_UPLOAD_SIZE) throw new Error(`File size (${bytesToMB(stat.size).toFixed(1)}MB) exceeds ${MAX_UPLOAD_SIZE_TEXT} limit: ${filePath}`);
14149
+ if (stat.size > MAX_UPLOAD_SIZE) throw new Error(t("errors.file.sizeExceeded", {
14150
+ size: bytesToMB(stat.size).toFixed(1),
14151
+ limit: MAX_UPLOAD_SIZE_TEXT,
14152
+ file: filePath
14153
+ }));
14128
14154
  const ext = path.extname(filePath).toLowerCase().replace(".", "");
14129
14155
  if (FILE_PART_EXTENSIONS.has(ext)) {
14130
14156
  if (shouldUseImageOcrFallback(aiConfig, modelOverride)) {
14131
14157
  const result = await recognizeImageText(filePath, aiConfig?.image);
14132
- consola.info(`Extracted image text via local OCR (confidence: ${(result.confidence * 100).toFixed(1)}%)`);
14158
+ consola.info(t("extract.file.ocrText", { confidence: (result.confidence * 100).toFixed(1) }));
14133
14159
  return { text: result.text };
14134
14160
  }
14135
14161
  return {
@@ -14141,16 +14167,19 @@ async function readExtractFileInput(filePath, aiConfig, modelOverride) {
14141
14167
  const buffer = await fs.readFile(filePath);
14142
14168
  const converter = createPdfConverter(aiConfig?.pdf);
14143
14169
  const result = await converter.convert(buffer, filePath);
14144
- if (result.metadata?.fallback === "true") consola.info(`Fell back to unpdf — ${result.pageCount} page(s) extracted`);
14145
- else consola.info(`Converted PDF via ${converter.name}, ${result.pageCount} page(s)`);
14170
+ if (result.metadata?.fallback === "true") consola.info(t("extract.file.pdfFallback", { count: result.pageCount }));
14171
+ else consola.info(t("extract.file.pdfConverted", {
14172
+ name: converter.name,
14173
+ count: result.pageCount
14174
+ }));
14146
14175
  const mdPath = filePath.replace(PDF_EXT_RE, ".md");
14147
14176
  try {
14148
14177
  await fs.writeFile(mdPath, result.text);
14149
- consola.info(`Markdown saved: ${mdPath}`);
14178
+ consola.info(t("extract.file.markdownSaved", { path: mdPath }));
14150
14179
  } catch {
14151
14180
  const fallbackMd = path.join(os.tmpdir(), `${path.basename(filePath, ".pdf")}.md`);
14152
14181
  await fs.writeFile(fallbackMd, result.text);
14153
- consola.info(`Markdown saved: ${fallbackMd}`);
14182
+ consola.info(t("extract.file.markdownSaved", { path: fallbackMd }));
14154
14183
  }
14155
14184
  return { text: result.text };
14156
14185
  }
@@ -14166,7 +14195,7 @@ async function extractSingle(aiexDir, config, aiConfig, schemaName, text$1, file
14166
14195
  };
14167
14196
  }
14168
14197
  const s = spinner();
14169
- if (!options?.quiet) s.start(filePath ? `Extracting from ${path.basename(filePath)}...` : "Extracting data...");
14198
+ if (!options?.quiet) s.start(filePath ? t("extract.file.extractedFrom", { file: path.basename(filePath) }) : t("extract.file.extracting"));
14170
14199
  const result = await extractStructuredData({
14171
14200
  config: aiConfig,
14172
14201
  schema: schemaLoad.schema,
@@ -14175,28 +14204,37 @@ async function extractSingle(aiexDir, config, aiConfig, schemaName, text$1, file
14175
14204
  file: filePath,
14176
14205
  modelOverride,
14177
14206
  onRetry(info) {
14178
- if (!options?.quiet) s.message(`API responded with ${info.statusCode}, retrying in ${info.delayMs / 1e3}s (${info.attempt}/${info.maxRetries})...`);
14207
+ if (!options?.quiet) s.message(t("extract.file.extractRetry", {
14208
+ code: info.statusCode,
14209
+ delay: info.delayMs / 1e3,
14210
+ attempt: info.attempt,
14211
+ max: info.maxRetries
14212
+ }));
14179
14213
  }
14180
14214
  });
14181
14215
  if (!result.success) {
14182
14216
  if (!options?.quiet) {
14183
- s.stop("Extraction failed");
14184
- consola.error(result.error || "Unknown error");
14217
+ s.stop(t("extract.file.extractFail"));
14218
+ consola.error(result.error || t("common.unknownError"));
14185
14219
  }
14186
14220
  return {
14187
14221
  success: false,
14188
- error: result.error || "Unknown error"
14222
+ error: result.error || t("common.unknownError")
14189
14223
  };
14190
14224
  }
14191
- if (!options?.quiet) s.stop("Extraction complete");
14192
- if (result.outputPath && !options?.quiet) consola.success(`Result saved: ${pc.cyan(result.outputPath)}`);
14193
- if (result.tokensUsed && !options?.quiet) consola.info(pc.gray(`Token usage: prompt=${result.tokensUsed.prompt}, completion=${result.tokensUsed.completion}, total=${result.tokensUsed.total}`));
14225
+ if (!options?.quiet) s.stop(t("extract.file.extractComplete"));
14226
+ if (result.outputPath && !options?.quiet) consola.success(t("extract.file.resultSaved", { path: pc.cyan(result.outputPath) }));
14227
+ if (result.tokensUsed && !options?.quiet) consola.info(pc.gray(t("extract.file.tokenUsage", {
14228
+ prompt: result.tokensUsed.prompt,
14229
+ completion: result.tokensUsed.completion,
14230
+ total: result.tokensUsed.total
14231
+ })));
14194
14232
  if (result.data && options?.insert !== false) {
14195
14233
  const s2 = spinner();
14196
- if (!options?.quiet) s2.start("Inserting into database...");
14234
+ if (!options?.quiet) s2.start(t("extract.file.insertingDb"));
14197
14235
  const dbError = await ensureDatabaseReady(config.databasePath, schemaLoad.schema);
14198
14236
  if (dbError) {
14199
- if (!options?.quiet) s2.stop("Database not ready");
14237
+ if (!options?.quiet) s2.stop(t("extract.file.dbNotReady"));
14200
14238
  consola.error(dbError);
14201
14239
  return {
14202
14240
  success: false,
@@ -14208,7 +14246,7 @@ async function extractSingle(aiexDir, config, aiConfig, schemaName, text$1, file
14208
14246
  try {
14209
14247
  const insertResult = insertExtractedData(db, schemaLoad.schema, result.data);
14210
14248
  if (insertResult.success) {
14211
- if (!options?.quiet) s2.stop(`Inserted into ${insertResult.tablesInserted.length} table(s)`);
14249
+ if (!options?.quiet) s2.stop(t("extract.file.insertedTables", { count: insertResult.tablesInserted.length }));
14212
14250
  return {
14213
14251
  success: true,
14214
14252
  outputPath: result.outputPath,
@@ -14217,8 +14255,8 @@ async function extractSingle(aiexDir, config, aiConfig, schemaName, text$1, file
14217
14255
  tokensUsed: result.tokensUsed
14218
14256
  };
14219
14257
  } else {
14220
- if (!options?.quiet) s2.stop("Database insert failed");
14221
- consola.error(insertResult.error || "Unknown error");
14258
+ if (!options?.quiet) s2.stop(t("extract.file.dbInsertFail"));
14259
+ consola.error(insertResult.error || t("common.unknownError"));
14222
14260
  return {
14223
14261
  success: false,
14224
14262
  error: insertResult.error
@@ -14228,7 +14266,7 @@ async function extractSingle(aiexDir, config, aiConfig, schemaName, text$1, file
14228
14266
  db.close();
14229
14267
  }
14230
14268
  } catch (e) {
14231
- if (!options?.quiet) s2.stop("Database insert failed");
14269
+ if (!options?.quiet) s2.stop(t("extract.file.dbInsertFail"));
14232
14270
  consola.error(e instanceof Error ? e.message : String(e));
14233
14271
  return {
14234
14272
  success: false,
@@ -14262,12 +14300,18 @@ async function runAuditedExtraction(options) {
14262
14300
  try {
14263
14301
  fileHash = await getFileHash(source.filePath);
14264
14302
  } catch (e) {
14265
- if (!quiet) consola.warn(`Failed to calculate file hash for ${path.basename(source.filePath)}: ${e instanceof Error ? e.message : String(e)}`);
14303
+ if (!quiet) consola.warn(t("extract.file.hashWarning", {
14304
+ file: path.basename(source.filePath),
14305
+ error: e instanceof Error ? e.message : String(e)
14306
+ }));
14266
14307
  }
14267
14308
  if (fileHash && !isPlainTextFile && !force) {
14268
14309
  const existing = await findSucceededAuditByHash(aiexDir, schemaName, fileHash);
14269
14310
  if (existing) {
14270
- if (!quiet) consola.info(`File ${pc.cyan(path.basename(source.filePath))} (hash: ${fileHash.slice(0, 8)}) has already been processed successfully. Skipping.`);
14311
+ if (!quiet) consola.info(t("extract.file.alreadyProcessed", {
14312
+ file: pc.cyan(path.basename(source.filePath)),
14313
+ hash: fileHash.slice(0, 8)
14314
+ }));
14271
14315
  return {
14272
14316
  success: true,
14273
14317
  skipped: true,
@@ -14312,7 +14356,7 @@ async function runAuditedExtraction(options) {
14312
14356
  let notionPages;
14313
14357
  if (shouldSyncNotion(aiConfig, schemaName)) try {
14314
14358
  notionPages = await syncResultToNotion(aiConfig, schemaName, r.data);
14315
- if (!quiet) consola.success(`Synced to Notion: ${notionPages.length} page(s)`);
14359
+ if (!quiet) consola.success(t("extract.file.notionSynced", { count: notionPages.length }));
14316
14360
  } catch (error) {
14317
14361
  await updateExtractionAuditRecord(aiexDir, audit.id, {
14318
14362
  status: "failed",
@@ -14322,7 +14366,7 @@ async function runAuditedExtraction(options) {
14322
14366
  tokensUsed: r.tokensUsed,
14323
14367
  error: error instanceof Error ? error.message : String(error)
14324
14368
  });
14325
- if (!quiet) consola.error(`Notion sync failed: ${error instanceof Error ? error.message : String(error)}`);
14369
+ if (!quiet) consola.error(t("extract.file.notionSyncFail", { error: error instanceof Error ? error.message : String(error) }));
14326
14370
  return {
14327
14371
  success: false,
14328
14372
  error: error instanceof Error ? error.message : String(error),
@@ -14353,7 +14397,7 @@ async function runAuditedExtraction(options) {
14353
14397
  status: "failed",
14354
14398
  error: r.error || "Extraction failed"
14355
14399
  });
14356
- if (!quiet) consola.error(`Failed: ${r.error}`);
14400
+ if (!quiet) consola.error(t("extract.file.extractionFailed", { error: r.error }));
14357
14401
  return {
14358
14402
  success: false,
14359
14403
  error: r.error,
@@ -14368,7 +14412,10 @@ async function runAuditedExtraction(options) {
14368
14412
  });
14369
14413
  if (!quiet) {
14370
14414
  const name$1 = source.type === "file" ? path.basename(source.filePath) : "text input";
14371
- consola.error(`Error processing ${name$1}: ${e instanceof Error ? e.message : String(e)}`);
14415
+ consola.error(t("extract.file.errorProcessing", {
14416
+ name: name$1,
14417
+ error: e instanceof Error ? e.message : String(e)
14418
+ }));
14372
14419
  }
14373
14420
  return {
14374
14421
  success: false,
@@ -14394,13 +14441,13 @@ async function processOneFile(aiexDir, config, aiConfig, schemaName, filePath, m
14394
14441
  quiet: false
14395
14442
  });
14396
14443
  if (result.success) {
14397
- if (!result.skipped) consola.success(`Processed: ${path.basename(filePath)}`);
14444
+ if (!result.skipped) consola.success(t("extract.file.processSuccess", { file: path.basename(filePath) }));
14398
14445
  return true;
14399
14446
  }
14400
14447
  return false;
14401
14448
  }
14402
14449
  async function runBatchExtraction(aiexDir, config, aiConfig, schemaName, dir, globPattern, modelOverride, options) {
14403
- consola.info(`Scanning ${pc.cyan(dir)} for supported files...`);
14450
+ consola.info(t("extract.batch.scanning", { dir: pc.cyan(dir) }));
14404
14451
  let files;
14405
14452
  try {
14406
14453
  files = listSupportedFiles(dir, globPattern);
@@ -14409,28 +14456,36 @@ async function runBatchExtraction(aiexDir, config, aiConfig, schemaName, dir, gl
14409
14456
  ok: false,
14410
14457
  successCount: 0,
14411
14458
  failCount: 0,
14412
- error: `Cannot read directory: ${dir}`
14459
+ error: t("extract.batch.errors.cannotReadDir", { dir })
14413
14460
  };
14414
14461
  }
14415
14462
  if (files.length === 0) return {
14416
14463
  ok: false,
14417
14464
  successCount: 0,
14418
14465
  failCount: 0,
14419
- error: `No supported files found in ${dir}`
14466
+ error: t("extract.batch.errors.noSupportedFiles", { dir })
14420
14467
  };
14421
- consola.info(`Found ${files.length} file(s) to process`);
14468
+ consola.info(t("extract.batch.found", { count: files.length }));
14422
14469
  let successCount = 0;
14423
14470
  let failCount = 0;
14424
14471
  for (let i = 0; i < files.length; i++) {
14425
14472
  const file = files[i];
14426
- consola.info(`\n[${i + 1}/${files.length}] Processing: ${pc.cyan(path.basename(file))}`);
14473
+ consola.info(`\n${t("extract.batch.processing", {
14474
+ current: i + 1,
14475
+ total: files.length,
14476
+ file: pc.cyan(path.basename(file))
14477
+ })}`);
14427
14478
  if (await processOneFile(aiexDir, config, aiConfig, schemaName, file, modelOverride, {
14428
14479
  insert: options?.insert,
14429
14480
  force: options?.force
14430
14481
  })) successCount++;
14431
14482
  else failCount++;
14432
14483
  }
14433
- consola.info(`\nBatch complete: ${pc.green(`${successCount} succeeded`)}, ${pc.red(`${failCount} failed`)}, ${files.length} total`);
14484
+ consola.info(`\n${t("extract.batch.complete", {
14485
+ success: pc.green(successCount),
14486
+ fail: pc.red(failCount),
14487
+ total: files.length
14488
+ })}`);
14434
14489
  return {
14435
14490
  ok: true,
14436
14491
  successCount,
@@ -14443,34 +14498,35 @@ async function runBatchExtraction(aiexDir, config, aiConfig, schemaName, dir, gl
14443
14498
  const dumpCommand = defineCommand({
14444
14499
  meta: {
14445
14500
  name: "dump",
14446
- description: "Dump SQLite database table to Excel (.xlsx) or CSV (.csv)"
14501
+ description: t("command.dump.description")
14447
14502
  },
14448
14503
  args: {
14449
14504
  table: {
14450
14505
  type: "string",
14451
14506
  alias: "t",
14452
- description: "SQLite table name to export"
14507
+ description: t("command.dump.args.table")
14453
14508
  },
14454
14509
  schema: {
14455
14510
  type: "string",
14456
14511
  alias: "s",
14457
- description: "Schema name (without .json extension) to export"
14512
+ description: t("command.dump.args.schema")
14458
14513
  },
14459
14514
  format: {
14460
14515
  type: "string",
14461
14516
  alias: "f",
14462
- description: "Export format: csv or xlsx (default: inferred from output or csv)"
14517
+ description: t("command.dump.args.format")
14463
14518
  },
14464
14519
  output: {
14465
14520
  type: "string",
14466
14521
  alias: "o",
14467
- description: "Output file path (default: ./<tableName>.<format>)"
14522
+ description: t("command.dump.args.output")
14468
14523
  }
14469
14524
  },
14470
14525
  async run({ args }) {
14471
14526
  intro(pc.inverse(" aiex dump "));
14527
+ await initI18n();
14472
14528
  if (!args.table && !args.schema) {
14473
- failCommand("Either table name (--table / -t) or schema name (--schema / -s) is required");
14529
+ failCommand(t("command.dump.errors.tableOrSchemaRequired"));
14474
14530
  return;
14475
14531
  }
14476
14532
  const cwd = process.cwd();
@@ -14481,17 +14537,20 @@ const dumpCommand = defineCommand({
14481
14537
  if (args.schema) {
14482
14538
  const schemaLoad = await loadSchema(config, args.schema);
14483
14539
  if (!schemaLoad.schema) {
14484
- failCommand(schemaLoad.error || `Schema file for "${args.schema}" not found`);
14540
+ failCommand(schemaLoad.error || t("command.dump.errors.schemaNotFound", { name: args.schema }));
14485
14541
  return;
14486
14542
  }
14487
14543
  schema = schemaLoad.schema;
14488
14544
  const tName = schema.table?.name;
14489
14545
  if (!tName) {
14490
- failCommand(`Schema "${args.schema}" does not define a database table name.`);
14546
+ failCommand(t("command.dump.errors.noTableName", { name: args.schema }));
14491
14547
  return;
14492
14548
  }
14493
14549
  if (tableName && tableName !== tName) {
14494
- failCommand(`Specified table name "${tableName}" does not match schema table name "${tName}"`);
14550
+ failCommand(t("command.dump.errors.tableMismatch", {
14551
+ table: tableName,
14552
+ schemaTable: tName
14553
+ }));
14495
14554
  return;
14496
14555
  }
14497
14556
  tableName = tName;
@@ -14518,16 +14577,19 @@ const dumpCommand = defineCommand({
14518
14577
  }
14519
14578
  if (!format) format = "csv";
14520
14579
  if (format !== "csv" && format !== "xlsx") {
14521
- failCommand(`Unsupported dump format: "${format}". Supported formats: csv, xlsx`);
14580
+ failCommand(t("command.dump.errors.unsupportedFormat", { format }));
14522
14581
  return;
14523
14582
  }
14524
14583
  const resolvedOutput = outputPathArg ? path.resolve(outputPathArg) : path.resolve(cwd, `${tableName}.${format}`);
14525
14584
  if (!fs$1.existsSync(config.databasePath)) {
14526
- failCommand(`Database file not found at ${config.databasePath}. Please run "aiex schema" to create the database first.`);
14585
+ failCommand(t("command.dump.errors.dbNotFound", {
14586
+ path: config.databasePath,
14587
+ cmd: "aiex schema"
14588
+ }));
14527
14589
  return;
14528
14590
  }
14529
14591
  const s = spinner();
14530
- s.start(`Loading data from table "${tableName}"...`);
14592
+ s.start(t("command.dump.loading", { name: tableName }));
14531
14593
  let columns = [];
14532
14594
  let rows = [];
14533
14595
  try {
@@ -14536,8 +14598,11 @@ const dumpCommand = defineCommand({
14536
14598
  select name from sqlite_master
14537
14599
  where type = 'table' and name = ?
14538
14600
  `).get(tableName)) {
14539
- s.stop("Database query failed");
14540
- failCommand(`Table "${tableName}" not found in database. Run "aiex schema" first to migrate.`);
14601
+ s.stop(t("command.dump.dbQueryFailed"));
14602
+ failCommand(t("command.dump.errors.tableNotFound", {
14603
+ name: tableName,
14604
+ cmd: "aiex schema"
14605
+ }));
14541
14606
  db.close();
14542
14607
  return;
14543
14608
  }
@@ -14545,16 +14610,16 @@ const dumpCommand = defineCommand({
14545
14610
  rows = db.prepare(`select * from ${tableName}`).all();
14546
14611
  db.close();
14547
14612
  } catch (error) {
14548
- s.stop("Database query failed");
14613
+ s.stop(t("command.dump.dbQueryFailed"));
14549
14614
  failCommand(error instanceof Error ? error.message : String(error));
14550
14615
  return;
14551
14616
  }
14552
14617
  if (rows.length === 0) {
14553
- s.stop("Empty table");
14554
- consola.warn(`Table "${tableName}" is empty. Exporting empty file...`);
14555
- } else s.stop(`Loaded ${rows.length} row(s)`);
14618
+ s.stop(t("command.dump.emptyTable"));
14619
+ consola.warn(t("command.dump.errors.tableEmpty", { name: tableName }));
14620
+ } else s.stop(t("command.dump.loaded", { count: rows.length }));
14556
14621
  const s2 = spinner();
14557
- s2.start("Formatting data...");
14622
+ s2.start(t("command.dump.formatting"));
14558
14623
  const formattedRows = rows.map((row) => {
14559
14624
  const newRow = {};
14560
14625
  columns.forEach((col) => {
@@ -14580,9 +14645,12 @@ const dumpCommand = defineCommand({
14580
14645
  });
14581
14646
  return newRow;
14582
14647
  });
14583
- s2.stop("Data formatted");
14648
+ s2.stop(t("command.dump.formatted"));
14584
14649
  const s3 = spinner();
14585
- s3.start(`Writing ${format.toUpperCase()} file to ${resolvedOutput}...`);
14650
+ s3.start(t("command.dump.writing", {
14651
+ format: format.toUpperCase(),
14652
+ path: resolvedOutput
14653
+ }));
14586
14654
  try {
14587
14655
  const ws = XLSX.utils.json_to_sheet(formattedRows, { header: columns.map((col) => col.name) });
14588
14656
  const outputDir = path.dirname(resolvedOutput);
@@ -14595,14 +14663,17 @@ const dumpCommand = defineCommand({
14595
14663
  const csv = XLSX.utils.sheet_to_csv(ws);
14596
14664
  fs$1.writeFileSync(resolvedOutput, "" + csv, "utf8");
14597
14665
  }
14598
- s3.stop("Dump completed successfully");
14599
- consola.success(`Successfully dumped ${rows.length} row(s) to ${pc.cyan(resolvedOutput)}`);
14666
+ s3.stop(t("command.dump.dumpCompleted"));
14667
+ consola.success(t("command.dump.successMsg", {
14668
+ count: rows.length,
14669
+ path: pc.cyan(resolvedOutput)
14670
+ }));
14600
14671
  } catch (error) {
14601
- s3.stop("File write failed");
14672
+ s3.stop(t("command.dump.fileWriteFailed"));
14602
14673
  failCommand(error instanceof Error ? error.message : String(error));
14603
14674
  return;
14604
14675
  }
14605
- outro("Done!");
14676
+ outro(t("common.done"));
14606
14677
  }
14607
14678
  });
14608
14679
 
@@ -14628,15 +14699,15 @@ function formatSource(source) {
14628
14699
  async function loadConfiguredAI(aiexDir) {
14629
14700
  const aiConfig = await readAIConfig(aiexDir);
14630
14701
  if (!aiConfig) {
14631
- failCommand("AI configuration not found. Please run \"aiex web\" to configure AI settings first");
14702
+ failCommand(t("command.extract.errors.noAIConfig", { cmd: "aiex web" }));
14632
14703
  return null;
14633
14704
  }
14634
14705
  if (!aiConfig.provider.apiKey) {
14635
- failCommand("API Key not configured. Please configure AI settings in the Web interface first");
14706
+ failCommand(t("command.extract.errors.noApiKey"));
14636
14707
  return null;
14637
14708
  }
14638
14709
  if (!aiConfig.provider.models?.length) {
14639
- failCommand("No models configured. Please add at least one model in AI Settings");
14710
+ failCommand(t("command.extract.errors.noModels"));
14640
14711
  return null;
14641
14712
  }
14642
14713
  return aiConfig;
@@ -14645,7 +14716,10 @@ function resolveModelOverride(aiConfig, modelName) {
14645
14716
  if (!modelName) return void 0;
14646
14717
  const matched = aiConfig.provider.models.find((m) => m.name === modelName);
14647
14718
  if (!matched) {
14648
- failCommand(`Model "${modelName}" not found in configuration. Available models: ${aiConfig.provider.models.map((m) => m.name).join(", ")}`);
14719
+ failCommand(t("command.extract.errors.modelNotFound", {
14720
+ model: modelName,
14721
+ available: aiConfig.provider.models.map((m) => m.name).join(", ")
14722
+ }));
14649
14723
  return null;
14650
14724
  }
14651
14725
  return matched;
@@ -14653,13 +14727,13 @@ function resolveModelOverride(aiConfig, modelName) {
14653
14727
  const historyCommand = defineCommand({
14654
14728
  meta: {
14655
14729
  name: "history",
14656
- description: "List extraction audit records"
14730
+ description: t("command.extract.history.description")
14657
14731
  },
14658
14732
  async run() {
14659
14733
  const config = createMigrationConfig(process.cwd());
14660
14734
  const records = await listExtractionAuditRecords(path.dirname(config.schemaPath));
14661
14735
  if (records.length === 0) {
14662
- consola.info("No extraction history found");
14736
+ consola.info(t("command.extract.history.empty"));
14663
14737
  return;
14664
14738
  }
14665
14739
  for (const record of records) {
@@ -14671,22 +14745,22 @@ const historyCommand = defineCommand({
14671
14745
  const showCommand = defineCommand({
14672
14746
  meta: {
14673
14747
  name: "show",
14674
- description: "Show an extraction audit record"
14748
+ description: t("command.extract.show.description")
14675
14749
  },
14676
14750
  args: { id: {
14677
14751
  type: "string",
14678
- description: "Audit record id"
14752
+ description: t("command.extract.show.args.id")
14679
14753
  } },
14680
14754
  async run({ args }) {
14681
14755
  const id = getIdArg(args);
14682
14756
  if (!id) {
14683
- failCommand("Audit record id is required");
14757
+ failCommand(t("command.extract.history.errors.idRequired"));
14684
14758
  return;
14685
14759
  }
14686
14760
  const config = createMigrationConfig(process.cwd());
14687
14761
  const record = await readExtractionAuditRecord(path.dirname(config.schemaPath), id);
14688
14762
  if (!record) {
14689
- failCommand(`Extraction record not found: ${id}`);
14763
+ failCommand(t("command.extract.history.errors.recordNotFound", { id }));
14690
14764
  return;
14691
14765
  }
14692
14766
  consola.info(JSON.stringify(record, null, 2));
@@ -14695,31 +14769,32 @@ const showCommand = defineCommand({
14695
14769
  const retryCommand = defineCommand({
14696
14770
  meta: {
14697
14771
  name: "retry",
14698
- description: "Retry an extraction audit record"
14772
+ description: t("command.extract.retry.description")
14699
14773
  },
14700
14774
  args: {
14701
14775
  id: {
14702
14776
  type: "string",
14703
- description: "Audit record id"
14777
+ description: t("command.extract.retry.args.id")
14704
14778
  },
14705
14779
  noInsert: {
14706
14780
  type: "boolean",
14707
- description: "Extract and save JSON without inserting into SQLite",
14781
+ description: t("command.extract.retry.args.noInsert"),
14708
14782
  default: false
14709
14783
  }
14710
14784
  },
14711
14785
  async run({ args }) {
14712
14786
  intro(pc.inverse(" aiex extract retry "));
14787
+ await initI18n();
14713
14788
  const id = getIdArg(args);
14714
14789
  if (!id) {
14715
- failCommand("Audit record id is required");
14790
+ failCommand(t("command.extract.history.errors.idRequired"));
14716
14791
  return;
14717
14792
  }
14718
14793
  const config = createMigrationConfig(process.cwd());
14719
14794
  const aiexDir = path.dirname(config.schemaPath);
14720
14795
  const record = await readExtractionAuditRecord(aiexDir, id);
14721
14796
  if (!record) {
14722
- failCommand(`Extraction record not found: ${id}`);
14797
+ failCommand(t("command.extract.history.errors.recordNotFound", { id }));
14723
14798
  return;
14724
14799
  }
14725
14800
  const aiConfig = await loadConfiguredAI(aiexDir);
@@ -14742,7 +14817,7 @@ const retryCommand = defineCommand({
14742
14817
  failCommand(result.error);
14743
14818
  return;
14744
14819
  }
14745
- outro("Done!");
14820
+ outro(t("common.done"));
14746
14821
  } catch (error) {
14747
14822
  if (isMissingUploadFileError(error)) {
14748
14823
  failCommand(MISSING_UPLOAD_FILE_TEXT);
@@ -14755,30 +14830,30 @@ const retryCommand = defineCommand({
14755
14830
  const rmCommand = defineCommand({
14756
14831
  meta: {
14757
14832
  name: "rm",
14758
- description: "Delete an extraction audit record and cached upload"
14833
+ description: t("command.extract.rm.description")
14759
14834
  },
14760
14835
  args: { id: {
14761
14836
  type: "string",
14762
- description: "Audit record id"
14837
+ description: t("command.extract.rm.args.id")
14763
14838
  } },
14764
14839
  async run({ args }) {
14765
14840
  const id = getIdArg(args);
14766
14841
  if (!id) {
14767
- failCommand("Audit record id is required");
14842
+ failCommand(t("command.extract.history.errors.idRequired"));
14768
14843
  return;
14769
14844
  }
14770
14845
  const config = createMigrationConfig(process.cwd());
14771
14846
  if (!await deleteExtractionAuditRecord(path.dirname(config.schemaPath), id)) {
14772
- failCommand(`Extraction record not found: ${id}`);
14847
+ failCommand(t("command.extract.history.errors.recordNotFound", { id }));
14773
14848
  return;
14774
14849
  }
14775
- consola.success(`Deleted extraction record: ${id}`);
14850
+ consola.success(t("command.extract.history.deleted", { id }));
14776
14851
  }
14777
14852
  });
14778
14853
  const extractCommand = defineCommand({
14779
14854
  meta: {
14780
14855
  name: "extract",
14781
- description: "Extract structured data from text, images, or PDFs"
14856
+ description: t("command.extract.description")
14782
14857
  },
14783
14858
  subCommands: {
14784
14859
  history: historyCommand,
@@ -14790,46 +14865,47 @@ const extractCommand = defineCommand({
14790
14865
  schema: {
14791
14866
  type: "string",
14792
14867
  alias: "s",
14793
- description: "Schema name (without .json extension)"
14868
+ description: t("command.extract.args.schema")
14794
14869
  },
14795
14870
  file: {
14796
14871
  type: "string",
14797
14872
  alias: "f",
14798
- description: `File path to extract from. Supported: ${SUPPORTED_FILE_TYPES_TEXT}.`
14873
+ description: t("command.extract.args.file", { types: SUPPORTED_FILE_TYPES_TEXT })
14799
14874
  },
14800
14875
  model: {
14801
14876
  type: "string",
14802
14877
  alias: "m",
14803
- description: "AI model to use for extraction (overrides auto-selection)"
14878
+ description: t("command.extract.args.model")
14804
14879
  },
14805
14880
  dir: {
14806
14881
  type: "string",
14807
14882
  alias: "d",
14808
- description: "Directory containing files to batch extract"
14883
+ description: t("command.extract.args.dir")
14809
14884
  },
14810
14885
  glob: {
14811
14886
  type: "string",
14812
14887
  alias: "g",
14813
- description: "Glob pattern to filter files in batch mode (e.g. \"*.pdf\")"
14888
+ description: t("command.extract.args.glob")
14814
14889
  },
14815
14890
  noInsert: {
14816
14891
  type: "boolean",
14817
- description: "Extract and save JSON without inserting into SQLite",
14892
+ description: t("command.extract.args.noInsert"),
14818
14893
  default: false
14819
14894
  },
14820
14895
  force: {
14821
14896
  type: "boolean",
14822
- description: "Force re-extraction even if the file has already been processed successfully",
14897
+ description: t("command.extract.args.force"),
14823
14898
  default: false
14824
14899
  }
14825
14900
  },
14826
14901
  async run({ args, rawArgs }) {
14827
14902
  if (isExtractSubCommand(rawArgs)) return;
14828
14903
  intro(pc.inverse(" aiex extract "));
14904
+ await initI18n();
14829
14905
  const config = createMigrationConfig(process.cwd());
14830
14906
  const aiexDir = path.dirname(config.schemaPath);
14831
14907
  if (args.dir && args.file) {
14832
- failCommand("Cannot combine -f/--file with -d/--dir");
14908
+ failCommand(t("command.extract.errors.conflictFileDir"));
14833
14909
  return;
14834
14910
  }
14835
14911
  const aiConfig = await loadConfiguredAI(aiexDir);
@@ -14837,12 +14913,12 @@ const extractCommand = defineCommand({
14837
14913
  const modelOverride = resolveModelOverride(aiConfig, args.model);
14838
14914
  if (modelOverride === null) return;
14839
14915
  if (!args.schema && !args.file && !args.dir) {
14840
- if (await runInteractive(aiexDir, config, aiConfig, modelOverride)) outro("Done!");
14916
+ if (await runInteractive(aiexDir, config, aiConfig, modelOverride)) outro(t("common.done"));
14841
14917
  return;
14842
14918
  }
14843
14919
  if (args.dir) {
14844
14920
  if (!args.schema) {
14845
- failCommand("Schema name (-s) is required in batch mode");
14921
+ failCommand(t("command.extract.errors.schemaRequiredBatch"));
14846
14922
  return;
14847
14923
  }
14848
14924
  const result$1 = await runBatchExtraction(aiexDir, config, aiConfig, args.schema, args.dir, args.glob, modelOverride, {
@@ -14854,16 +14930,16 @@ const extractCommand = defineCommand({
14854
14930
  return;
14855
14931
  }
14856
14932
  if (result$1.failCount > 0) process.exitCode = 1;
14857
- if (result$1.failCount > 0) outro(`Completed with failures (${result$1.failCount} failed)`);
14858
- else outro("Done!");
14933
+ if (result$1.failCount > 0) outro(t("command.extract.batch.failSummary", { count: result$1.failCount }));
14934
+ else outro(t("common.done"));
14859
14935
  return;
14860
14936
  }
14861
14937
  if (!args.schema) {
14862
- failCommand("Please provide a schema name (-s) to extract from");
14938
+ failCommand(t("command.extract.errors.schemaRequiredSingle"));
14863
14939
  return;
14864
14940
  }
14865
14941
  if (!args.file) {
14866
- failCommand("Please provide a file (-f) to extract from");
14942
+ failCommand(t("command.extract.errors.fileRequiredSingle"));
14867
14943
  return;
14868
14944
  }
14869
14945
  const result = await runAuditedExtraction({
@@ -14884,51 +14960,54 @@ const extractCommand = defineCommand({
14884
14960
  failCommand(result.error);
14885
14961
  return;
14886
14962
  }
14887
- outro("Done!");
14963
+ outro(t("common.done"));
14888
14964
  }
14889
14965
  });
14890
14966
  async function runInteractive(aiexDir, config, aiConfig, modelOverride) {
14891
14967
  const schemas = await listSchemas(aiexDir);
14892
14968
  if (schemas.length === 0) {
14893
- failCommand(`No schema files found in ${pc.cyan(".aiex/schema/")}. Run ${pc.cyan("aiex web")} to create and configure schemas first.`);
14969
+ failCommand(t("command.extract.errors.noSchemas", {
14970
+ path: pc.cyan(".aiex/schema/"),
14971
+ cmd: pc.cyan("aiex web")
14972
+ }));
14894
14973
  return false;
14895
14974
  }
14896
14975
  const schemaName = await select({
14897
- message: "Select a schema to extract data for:",
14976
+ message: t("command.extract.interactive.selectSchema"),
14898
14977
  options: schemas.map((s) => ({
14899
14978
  label: s,
14900
14979
  value: s
14901
14980
  }))
14902
14981
  });
14903
14982
  if (isCancel(schemaName)) {
14904
- cancel("Cancelled");
14983
+ cancel(t("common.cancelled"));
14905
14984
  return false;
14906
14985
  }
14907
14986
  const inputSource = await select({
14908
- message: "Choose input source:",
14987
+ message: t("command.extract.interactive.chooseSource"),
14909
14988
  options: [{
14910
- label: "Single file",
14989
+ label: t("command.extract.interactive.singleFile"),
14911
14990
  value: "file",
14912
- hint: "Extract from a file (txt, pdf, image)"
14991
+ hint: t("command.extract.interactive.singleFileHint")
14913
14992
  }, {
14914
- label: "Batch directory",
14993
+ label: t("command.extract.interactive.batchDir"),
14915
14994
  value: "dir",
14916
- hint: "Extract all supported files in a directory"
14995
+ hint: t("command.extract.interactive.batchDirHint")
14917
14996
  }]
14918
14997
  });
14919
14998
  if (isCancel(inputSource)) {
14920
- cancel("Cancelled");
14999
+ cancel(t("common.cancelled"));
14921
15000
  return false;
14922
15001
  }
14923
15002
  if (inputSource === "file") {
14924
15003
  const filePathStr = await text({
14925
- message: "Enter file path:",
15004
+ message: t("command.extract.interactive.enterFilePath"),
14926
15005
  validate(value) {
14927
- if (!value || value.trim().length === 0) return "Please enter a file path";
15006
+ if (!value || value.trim().length === 0) return t("command.extract.interactive.filePathRequired");
14928
15007
  }
14929
15008
  });
14930
15009
  if (isCancel(filePathStr)) {
14931
- cancel("Cancelled");
15010
+ cancel(t("common.cancelled"));
14932
15011
  return false;
14933
15012
  }
14934
15013
  return (await runAuditedExtraction({
@@ -14944,13 +15023,13 @@ async function runInteractive(aiexDir, config, aiConfig, modelOverride) {
14944
15023
  })).success;
14945
15024
  } else if (inputSource === "dir") {
14946
15025
  const dirPath = await text({
14947
- message: "Enter directory path:",
15026
+ message: t("command.extract.interactive.enterDirPath"),
14948
15027
  validate(value) {
14949
- if (!value || value.trim().length === 0) return "Please enter a directory path";
15028
+ if (!value || value.trim().length === 0) return t("command.extract.interactive.dirPathRequired");
14950
15029
  }
14951
15030
  });
14952
15031
  if (isCancel(dirPath)) {
14953
- cancel("Cancelled");
15032
+ cancel(t("common.cancelled"));
14954
15033
  return false;
14955
15034
  }
14956
15035
  const result = await runBatchExtraction(aiexDir, config, aiConfig, schemaName, dirPath, void 0, modelOverride);
@@ -14961,7 +15040,7 @@ async function runInteractive(aiexDir, config, aiConfig, modelOverride) {
14961
15040
  }
14962
15041
  function cancel(msg) {
14963
15042
  consola.info(msg);
14964
- outro("Cancelled");
15043
+ outro(t("common.cancelled"));
14965
15044
  process.exitCode = 0;
14966
15045
  }
14967
15046
 
@@ -15006,18 +15085,18 @@ function parseMigrationOutput(stdout, stderr) {
15006
15085
  const jsonLine = stdout.trim().split("\n").find((l) => l.startsWith("{") && l.endsWith("}"));
15007
15086
  if (!jsonLine) return {
15008
15087
  success: false,
15009
- error: "Migration helper did not return valid output"
15088
+ error: t("errors.schema.migrationHelperInvalidOutput")
15010
15089
  };
15011
15090
  const result = JSON.parse(jsonLine);
15012
15091
  if (!result.success) return {
15013
15092
  success: false,
15014
- error: result.error || "Migration failed"
15093
+ error: result.error || t("errors.schema.migrationFailed")
15015
15094
  };
15016
15095
  return result;
15017
15096
  } catch {
15018
15097
  return {
15019
15098
  success: false,
15020
- error: stderr || stdout || "Migration helper failed"
15099
+ error: stderr || stdout || t("errors.schema.migrationHelperFailed")
15021
15100
  };
15022
15101
  }
15023
15102
  }
@@ -15046,7 +15125,7 @@ async function runSchemaSync(config, options = {}) {
15046
15125
  const schemaFiles = await listSchemaFiles(config.schemaPath);
15047
15126
  if (schemaFiles.length === 0) return {
15048
15127
  success: false,
15049
- error: "No schema files found",
15128
+ error: t("errors.schema.noFiles"),
15050
15129
  warnings: [],
15051
15130
  schemaCount: 0,
15052
15131
  tables: 0,
@@ -15085,61 +15164,65 @@ async function runSchemaSync(config, options = {}) {
15085
15164
  const schemaCommand = defineCommand({
15086
15165
  meta: {
15087
15166
  name: "schema",
15088
- description: "Sync JSON Schema to SQLite database"
15167
+ description: t("command.schema.description")
15089
15168
  },
15090
15169
  args: {
15091
15170
  generate: {
15092
15171
  type: "boolean",
15093
15172
  alias: "g",
15094
- description: "Only generate Drizzle schema, skip migrate",
15173
+ description: t("command.schema.args.generate"),
15095
15174
  default: false
15096
15175
  },
15097
15176
  name: {
15098
15177
  type: "string",
15099
- description: "Name for the migration"
15178
+ description: t("command.schema.args.name")
15100
15179
  }
15101
15180
  },
15102
15181
  async run({ args }) {
15103
15182
  intro(pc.inverse(" aiex schema "));
15183
+ await initI18n();
15104
15184
  const config = createMigrationConfig(process.cwd());
15105
15185
  const schemaFiles = await listSchemaFiles(config.schemaPath);
15106
15186
  if (schemaFiles.length === 0) {
15107
- consola.info(`Run ${pc.cyan("aiex web")} to create and configure schemas in the Web UI`);
15108
- failCommand(`No schema files found in ${pc.cyan(".aiex/schema/")}`);
15187
+ consola.info(t("command.schema.runWebHint", { cmd: pc.cyan("aiex web") }));
15188
+ failCommand(t("command.schema.noSchemas", { path: pc.cyan(".aiex/schema/") }));
15109
15189
  return;
15110
15190
  }
15111
15191
  const s1 = spinner();
15112
- s1.start("Generating Drizzle schema...");
15192
+ s1.start(t("command.schema.generating"));
15113
15193
  const generated = await generateSchemaFromFiles(schemaFiles, config);
15114
15194
  for (const warning of generated.warnings) consola.warn(warning);
15115
- if (generated.success) consola.success(`Generated ${pc.cyan(".aiex/drizzle/schema.ts")} from ${generated.schemaCount} schema file(s)`);
15195
+ if (generated.success) consola.success(t("command.schema.generated", {
15196
+ path: pc.cyan(".aiex/drizzle/schema.ts"),
15197
+ count: generated.schemaCount
15198
+ }));
15116
15199
  else if (generated.error) consola.error(generated.error);
15117
- s1.stop(generated.success ? "Schema generated" : "Generation failed");
15200
+ s1.stop(generated.success ? t("command.schema.generatedOk") : t("command.schema.generatedFail"));
15118
15201
  if (!generated.success) {
15119
- failCommand();
15202
+ failCommand(t("common.failed"));
15120
15203
  return;
15121
15204
  }
15122
15205
  if (args.generate) {
15123
- outro("Done! Run without --generate to apply migrations");
15206
+ outro(t("command.schema.runWithoutGenerate"));
15124
15207
  return;
15125
15208
  }
15126
15209
  const s2 = spinner();
15127
- s2.start("Running migrations...");
15210
+ s2.start(t("command.schema.runningMigrations"));
15128
15211
  const migration = await runSchemaMigration(config, args.name);
15129
15212
  if (!migration.success) {
15130
- consola.error("Failed to generate migration");
15131
- consola.error(migration.error || "Migration failed");
15132
- } else if (migration.changes === 0) consola.info(pc.gray("No changes detected"));
15213
+ consola.error(t("command.schema.migrationFailed"));
15214
+ consola.error(migration.error || t("command.schema.migrationFail"));
15215
+ } else if (migration.changes === 0) consola.info(pc.gray(t("command.schema.noChanges")));
15133
15216
  else {
15134
- consola.success(pc.green("Migration files generated"));
15135
- consola.success(pc.green("Database migrated"));
15217
+ consola.success(pc.green(t("command.schema.migrationFilesGenerated")));
15218
+ consola.success(pc.green(t("command.schema.databaseMigrated")));
15136
15219
  }
15137
- s2.stop(migration.success ? "Migrations applied" : "Migration failed");
15220
+ s2.stop(migration.success ? t("command.schema.migrationsApplied") : t("command.schema.migrationFail"));
15138
15221
  if (!migration.success) {
15139
- failCommand();
15222
+ failCommand(t("common.failed"));
15140
15223
  return;
15141
15224
  }
15142
- outro("Done!");
15225
+ outro(t("common.done"));
15143
15226
  }
15144
15227
  });
15145
15228
 
@@ -15208,14 +15291,14 @@ var WatchRegistry = class {
15208
15291
  };
15209
15292
  async function notifySuccess(fileName) {
15210
15293
  if (process.platform === "darwin") try {
15211
- await execa("osascript", ["-e", `display notification "Successfully processed and inserted data." with title "AIEX Watch: ${fileName}"`]);
15294
+ await execa("osascript", ["-e", `display notification "${t("command.watch.notification.success")}" with title "${t("command.watch.notification.successTitle", { file: fileName })}"`]);
15212
15295
  await execa("afplay", ["/System/Library/Sounds/Glass.aiff"]);
15213
15296
  } catch {}
15214
15297
  else process.stdout.write("\x07");
15215
15298
  }
15216
15299
  async function notifyFailure(fileName, errorMessage) {
15217
15300
  if (process.platform === "darwin") try {
15218
- await execa("osascript", ["-e", `display notification "${errorMessage.replace(/"/g, "\\\"")}" with title "AIEX Watch Failed: ${fileName}"`]);
15301
+ await execa("osascript", ["-e", `display notification "${errorMessage.replace(/"/g, "\\\"")}" with title "${t("command.watch.notification.failTitle", { file: fileName })}"`]);
15219
15302
  await execa("afplay", ["/System/Library/Sounds/Basso.aiff"]);
15220
15303
  } catch {}
15221
15304
  else process.stdout.write("\x07\x07");
@@ -15227,9 +15310,9 @@ function startWatcher(options) {
15227
15310
  const registry$2 = new WatchRegistry(aiexDir);
15228
15311
  fs$1.mkdirSync(queueDirActive, { recursive: true });
15229
15312
  fs$1.mkdirSync(queueDirFailed, { recursive: true });
15230
- consola.info(pc.green(`Starting watch on folder: ${pc.cyan(watchDir)}`));
15231
- consola.info(pc.green(`Schema: ${pc.cyan(schemaName)}`));
15232
- if (modelOverride) consola.info(pc.green(`Model Override: ${pc.cyan(modelOverride.name)}`));
15313
+ consola.info(pc.green(t("command.watch.starting.watchFolder", { dir: pc.cyan(watchDir) })));
15314
+ consola.info(pc.green(t("command.watch.starting.schema", { name: pc.cyan(schemaName) })));
15315
+ if (modelOverride) consola.info(pc.green(t("command.watch.starting.modelOverride", { name: pc.cyan(modelOverride.name) })));
15233
15316
  const watcher = chokidar.watch(watchDir, {
15234
15317
  persistent: true,
15235
15318
  ignoreInitial: false,
@@ -15245,15 +15328,18 @@ function startWatcher(options) {
15245
15328
  if (!stat || !stat.isFile()) return;
15246
15329
  const ext = path.extname(resolvedPath).toLowerCase().replace(".", "");
15247
15330
  if (!SUPPORTED_EXTENSIONS.has(ext)) {
15248
- consola.warn(`[Watcher] Skipped unsupported file type: ${path.basename(resolvedPath)}`);
15331
+ consola.warn(t("command.watch.events.skippedUnsupported", { file: path.basename(resolvedPath) }));
15249
15332
  return;
15250
15333
  }
15251
15334
  const fileName = path.basename(resolvedPath);
15252
- consola.info(`[Watcher] New file detected: ${pc.cyan(fileName)}. Processing...`);
15335
+ consola.info(t("command.watch.events.fileDetected", { file: pc.cyan(fileName) }));
15253
15336
  try {
15254
15337
  const hash = await getFileHash(resolvedPath);
15255
15338
  if (await registry$2.getStatus(hash) === "succeeded") {
15256
- consola.info(`[Watcher] File ${pc.cyan(fileName)} (hash: ${hash.slice(0, 8)}) has already been processed successfully. Skipping.`);
15339
+ consola.info(t("command.watch.events.alreadyProcessed", {
15340
+ file: pc.cyan(fileName),
15341
+ hash: hash.slice(0, 8)
15342
+ }));
15257
15343
  return;
15258
15344
  }
15259
15345
  const activeQueuePath = path.join(queueDirActive, `${hash}.${ext}`);
@@ -15262,25 +15348,28 @@ function startWatcher(options) {
15262
15348
  await registry$2.markSucceeded(hash, resolvedPath);
15263
15349
  await fs.rm(activeQueuePath, { force: true }).catch(() => {});
15264
15350
  await fs.rm(activeQueuePath.replace(PDF_EXT_REGEXP, ".md"), { force: true }).catch(() => {});
15265
- consola.success(`[Watcher] File processed successfully: ${pc.green(fileName)}`);
15351
+ consola.success(t("command.watch.events.processedSuccess", { file: pc.green(fileName) }));
15266
15352
  await notifySuccess(fileName);
15267
15353
  } else {
15268
- const errorMsg = "Extraction failed. See extraction audit history.";
15354
+ const errorMsg = t("command.watch.events.extractionFailed");
15269
15355
  await registry$2.markFailed(hash, resolvedPath, errorMsg);
15270
15356
  const failedQueuePath = path.join(queueDirFailed, `${hash}-${Date.now()}.${ext}`);
15271
15357
  await fs.rename(activeQueuePath, failedQueuePath).catch(() => {});
15272
15358
  await fs.rm(activeQueuePath.replace(PDF_EXT_REGEXP, ".md"), { force: true }).catch(() => {});
15273
- consola.error(`[Watcher] Processing failed for: ${pc.red(fileName)}`);
15359
+ consola.error(t("command.watch.events.processingFailed", { file: pc.red(fileName) }));
15274
15360
  await notifyFailure(fileName, errorMsg);
15275
15361
  }
15276
15362
  } catch (e) {
15277
15363
  const errorMsg = e instanceof Error ? e.message : String(e);
15278
- consola.error(`[Watcher] Error processing file ${fileName}: ${errorMsg}`);
15364
+ consola.error(t("command.watch.events.errorProcessing", {
15365
+ file: fileName,
15366
+ error: errorMsg
15367
+ }));
15279
15368
  await notifyFailure(fileName, errorMsg);
15280
15369
  }
15281
15370
  });
15282
15371
  watcher.on("error", (error) => {
15283
- consola.error(`[Watcher] Watcher error: ${error?.message || String(error)}`);
15372
+ consola.error(t("command.watch.events.watcherError", { error: error?.message || String(error) }));
15284
15373
  });
15285
15374
  return watcher;
15286
15375
  }
@@ -15290,56 +15379,60 @@ function startWatcher(options) {
15290
15379
  const watchCommand = defineCommand({
15291
15380
  meta: {
15292
15381
  name: "watch",
15293
- description: "Watch a directory for new files and automatically extract data"
15382
+ description: t("command.watch.description")
15294
15383
  },
15295
15384
  args: {
15296
15385
  schema: {
15297
15386
  type: "string",
15298
15387
  alias: "s",
15299
- description: "Schema name (without .json extension) to use for extraction"
15388
+ description: t("command.watch.args.schema")
15300
15389
  },
15301
15390
  dir: {
15302
15391
  type: "string",
15303
15392
  alias: "d",
15304
- description: "Directory path to watch for incoming files"
15393
+ description: t("command.watch.args.dir")
15305
15394
  },
15306
15395
  model: {
15307
15396
  type: "string",
15308
15397
  alias: "m",
15309
- description: "AI model to use for extraction (overrides default/auto-selected model)"
15398
+ description: t("command.watch.args.model")
15310
15399
  },
15311
15400
  noInsert: {
15312
15401
  type: "boolean",
15313
- description: "Extract and save JSON without inserting into SQLite database",
15402
+ description: t("command.watch.args.noInsert"),
15314
15403
  default: false
15315
15404
  }
15316
15405
  },
15317
15406
  async run({ args }) {
15318
15407
  intro(pc.inverse(" aiex watch "));
15408
+ await initI18n();
15319
15409
  if (!args.schema) {
15320
- failCommand("Schema name (-s) is required");
15410
+ failCommand(t("command.watch.errors.schemaRequired"));
15321
15411
  return;
15322
15412
  }
15323
15413
  if (!args.dir) {
15324
- failCommand("Watch directory path (-d) is required");
15414
+ failCommand(t("command.watch.errors.dirRequired"));
15325
15415
  return;
15326
15416
  }
15327
15417
  const config = createMigrationConfig(process.cwd());
15328
15418
  const aiexDir = path.dirname(config.schemaPath);
15329
15419
  const schemaLoad = await loadSchema(config, args.schema);
15330
15420
  if (!schemaLoad.schema) {
15331
- failCommand(schemaLoad.error || `Schema file for "${args.schema}" not found`);
15421
+ failCommand(schemaLoad.error || t("command.watch.errors.schemaNotFound", { name: args.schema }));
15332
15422
  return;
15333
15423
  }
15334
15424
  let watchDirStat;
15335
15425
  try {
15336
15426
  watchDirStat = fs$1.statSync(args.dir);
15337
15427
  } catch (e) {
15338
- failCommand(`Watch directory does not exist: ${args.dir} — ${e instanceof Error ? e.message : String(e)}`);
15428
+ failCommand(t("command.watch.errors.dirNotExist", {
15429
+ dir: args.dir,
15430
+ error: e instanceof Error ? e.message : String(e)
15431
+ }));
15339
15432
  return;
15340
15433
  }
15341
15434
  if (!watchDirStat.isDirectory()) {
15342
- failCommand(`Watch path is not a directory: ${args.dir}`);
15435
+ failCommand(t("command.watch.errors.notADirectory", { dir: args.dir }));
15343
15436
  return;
15344
15437
  }
15345
15438
  const watchDirAbs = path.resolve(args.dir);
@@ -15357,14 +15450,14 @@ const watchCommand = defineCommand({
15357
15450
  insert: !args.noInsert
15358
15451
  });
15359
15452
  const cleanup = async () => {
15360
- consola.info("\nStopping watch directory daemon...");
15453
+ consola.info(t("command.watch.events.stopped"));
15361
15454
  await watcher.close();
15362
- consola.success("Daemon stopped.");
15455
+ consola.success(t("command.watch.events.stoppedOk"));
15363
15456
  process.exit(0);
15364
15457
  };
15365
15458
  process.on("SIGINT", cleanup);
15366
15459
  process.on("SIGTERM", cleanup);
15367
- consola.info("Press Ctrl+C to stop");
15460
+ consola.info(t("command.watch.events.pressCtrlC"));
15368
15461
  }
15369
15462
  });
15370
15463
 
@@ -15430,7 +15523,7 @@ function aiRoutes(config) {
15430
15523
  const schemaName = typeof body.schemaName === "string" ? body.schemaName : "";
15431
15524
  if (!schemaName) return c.json({
15432
15525
  success: false,
15433
- error: "Schema is required"
15526
+ error: t("server.schemaRequired")
15434
15527
  }, 400);
15435
15528
  const result = await inspectNotionDatabase({
15436
15529
  token,
@@ -15455,26 +15548,26 @@ function aiRoutes(config) {
15455
15548
  const userTpl = body?.prompt?.userTemplate;
15456
15549
  if (!systemTpl || !systemTpl.includes("{schema}")) return c.json({
15457
15550
  success: false,
15458
- error: "System prompt must contain the {schema} placeholder"
15551
+ error: t("server.promptSchemaPlaceholder")
15459
15552
  }, 400);
15460
15553
  if (!userTpl?.includes("{text}")) return c.json({
15461
15554
  success: false,
15462
- error: "User prompt must contain the {text} placeholder"
15555
+ error: t("server.promptTextPlaceholder")
15463
15556
  }, 400);
15464
15557
  if (!body.provider?.models?.length) return c.json({
15465
15558
  success: false,
15466
- error: "At least one model must be configured"
15559
+ error: t("server.atLeastOneModel")
15467
15560
  }, 400);
15468
15561
  if (body.notion?.enabled) {
15469
15562
  if (!body.notion.token?.trim()) return c.json({
15470
15563
  success: false,
15471
- error: "Notion token is required when Notion export is enabled"
15564
+ error: t("server.notionTokenRequired")
15472
15565
  }, 400);
15473
15566
  for (const [schemaName, schemaConfig] of Object.entries(body.notion.schemas ?? {})) {
15474
15567
  if (typeof schemaConfig.databaseId === "string") schemaConfig.databaseId = parseNotionDatabaseId(schemaConfig.databaseId);
15475
15568
  if (!schemaConfig.databaseId?.trim()) return c.json({
15476
15569
  success: false,
15477
- error: `Notion database ID is required for schema "${schemaName}"`
15570
+ error: t("server.notionDbIdRequired", { name: schemaName })
15478
15571
  }, 400);
15479
15572
  }
15480
15573
  }
@@ -15626,21 +15719,21 @@ function dataRoutes(config) {
15626
15719
  return c.json({ error: error instanceof Error ? error.message : String(error) }, 500);
15627
15720
  }
15628
15721
  });
15629
- app.get("/data/tables/:name", zValidator("param", tableParamSchema, invalidParamResponse$1("Invalid table name")), zValidator("query", tableQuerySchema), async (c) => {
15722
+ app.get("/data/tables/:name", zValidator("param", tableParamSchema, invalidParamResponse$1(t("server.invalidTableName"))), zValidator("query", tableQuerySchema), async (c) => {
15630
15723
  const { name: tableName } = c.req.valid("param");
15631
15724
  const { page, pageSize, search, sortField, sortOrder, all } = c.req.valid("query");
15632
15725
  let db;
15633
15726
  try {
15634
15727
  db = createReadonlyQueryDb(config.databasePath);
15635
15728
  } catch {
15636
- return c.json({ error: "Database not found. Run `aiex schema` first." }, 400);
15729
+ return c.json({ error: t("server.dbNotFound") }, 400);
15637
15730
  }
15638
15731
  try {
15639
15732
  if ((await sql`
15640
15733
  select name
15641
15734
  from sqlite_master
15642
15735
  where type = 'table' and name = ${tableName}
15643
- `.execute(db)).rows.length === 0) return c.json({ error: `Table "${tableName}" not found in database` }, 404);
15736
+ `.execute(db)).rows.length === 0) return c.json({ error: t("server.tableNotFound", { name: tableName }) }, 404);
15644
15737
  const columns = (await sql`
15645
15738
  pragma table_info(${sql.table(tableName)})
15646
15739
  `.execute(db)).rows.map((col) => ({
@@ -15708,7 +15801,7 @@ function dataRoutes(config) {
15708
15801
  await db.destroy();
15709
15802
  }
15710
15803
  });
15711
- app.get("/data/:name", zValidator("param", extractionFileParamSchema, invalidParamResponse$1("Invalid extraction file name")), async (c) => {
15804
+ app.get("/data/:name", zValidator("param", extractionFileParamSchema, invalidParamResponse$1(t("server.invalidFileName"))), async (c) => {
15712
15805
  const { name: name$1 } = c.req.valid("param");
15713
15806
  const filePath = path.join(extractedDir, name$1);
15714
15807
  try {
@@ -15719,31 +15812,31 @@ function dataRoutes(config) {
15719
15812
  name: name$1
15720
15813
  });
15721
15814
  } catch {
15722
- return c.json({ error: "Extraction result not found" }, 404);
15815
+ return c.json({ error: t("server.extractionNotFound") }, 404);
15723
15816
  }
15724
15817
  });
15725
- app.post("/data/:name/notion/retry", zValidator("param", extractionFileParamSchema, invalidParamResponse$1("Invalid extraction file name")), async (c) => {
15818
+ app.post("/data/:name/notion/retry", zValidator("param", extractionFileParamSchema, invalidParamResponse$1(t("server.invalidFileName"))), async (c) => {
15726
15819
  const { name: name$1 } = c.req.valid("param");
15727
15820
  const filePath = path.join(extractedDir, name$1);
15728
15821
  const schemaName = schemaNameFromExtractionFile(name$1);
15729
15822
  if (!schemaName) return c.json({
15730
15823
  success: false,
15731
- error: "Cannot infer schema name from extraction file name"
15824
+ error: t("server.cannotInferSchema")
15732
15825
  }, 400);
15733
15826
  const aiConfig = await readAIConfig(aiexDir);
15734
15827
  if (!aiConfig?.notion?.enabled) return c.json({
15735
15828
  success: false,
15736
- error: "Notion export is not enabled. Configure Notion settings first."
15829
+ error: t("errors.notion.notEnabled")
15737
15830
  }, 400);
15738
15831
  if (!aiConfig.notion.schemas?.[schemaName]?.databaseId?.trim()) return c.json({
15739
15832
  success: false,
15740
- error: `Notion database is not configured for schema "${schemaName}".`
15833
+ error: t("errors.notion.noSchemaConfig", { name: schemaName })
15741
15834
  }, 400);
15742
15835
  try {
15743
15836
  const data = await readFile(filePath);
15744
15837
  if (!data || typeof data !== "object" || Array.isArray(data)) return c.json({
15745
15838
  success: false,
15746
- error: "Extraction result is not a JSON object and cannot be written to Notion."
15839
+ error: t("errors.ai.extractionNotObject")
15747
15840
  }, 400);
15748
15841
  const page = await writeNotionPage(aiConfig.notion, schemaName, data);
15749
15842
  const notionPages = [{
@@ -15837,15 +15930,15 @@ function extractRoutes(config) {
15837
15930
  const file = getFormFile(body.file);
15838
15931
  if (!schemaName) return c.json({
15839
15932
  success: false,
15840
- error: "Schema is required"
15933
+ error: t("server.schemaRequired")
15841
15934
  }, 400);
15842
15935
  if (!text$1 && !file) return c.json({
15843
15936
  success: false,
15844
- error: "Provide text or upload a file to extract"
15937
+ error: t("server.provideTextOrFile")
15845
15938
  }, 400);
15846
15939
  if (text$1 && file) return c.json({
15847
15940
  success: false,
15848
- error: "Text and file input cannot be used together"
15941
+ error: t("server.conflictTextAndFile")
15849
15942
  }, 400);
15850
15943
  let source;
15851
15944
  if (file) {
@@ -15871,20 +15964,20 @@ function extractRoutes(config) {
15871
15964
  const aiConfig = await readAIConfig(aiexDir);
15872
15965
  if (!aiConfig) return c.json({
15873
15966
  success: false,
15874
- error: "AI configuration not found. Configure AI settings first."
15967
+ error: t("server.aiConfigNotFound")
15875
15968
  }, 400);
15876
15969
  if (!aiConfig.provider.apiKey) return c.json({
15877
15970
  success: false,
15878
- error: "API Key not configured. Configure AI settings first."
15971
+ error: t("server.apiKeyNotConfigured")
15879
15972
  }, 400);
15880
15973
  if (!aiConfig.provider.models?.length) return c.json({
15881
15974
  success: false,
15882
- error: "No models configured. Add at least one model in AI Settings."
15975
+ error: t("server.noModelsConfigured")
15883
15976
  }, 400);
15884
15977
  const modelOverride = modelName ? aiConfig.provider.models.find((model) => model.name === modelName) : void 0;
15885
15978
  if (modelName && !modelOverride) return c.json({
15886
15979
  success: false,
15887
- error: `Model "${modelName}" not found in AI settings`
15980
+ error: t("server.modelNotFound", { name: modelName })
15888
15981
  }, 400);
15889
15982
  const result = await runAuditedExtraction({
15890
15983
  aiexDir,
@@ -15924,25 +16017,25 @@ function extractRoutes(config) {
15924
16017
  const original = await readExtractionAuditRecord(aiexDir, c.req.param("id"));
15925
16018
  if (!original) return c.json({
15926
16019
  success: false,
15927
- error: "Extraction record not found"
16020
+ error: t("server.extractionRecordNotFound")
15928
16021
  }, 404);
15929
16022
  const aiConfig = await readAIConfig(aiexDir);
15930
16023
  if (!aiConfig) return c.json({
15931
16024
  success: false,
15932
- error: "AI configuration not found. Configure AI settings first."
16025
+ error: t("server.aiConfigNotFound")
15933
16026
  }, 400);
15934
16027
  if (!aiConfig.provider.apiKey) return c.json({
15935
16028
  success: false,
15936
- error: "API Key not configured. Configure AI settings first."
16029
+ error: t("server.apiKeyNotConfigured")
15937
16030
  }, 400);
15938
16031
  if (!aiConfig.provider.models?.length) return c.json({
15939
16032
  success: false,
15940
- error: "No models configured. Add at least one model in AI Settings."
16033
+ error: t("server.noModelsConfigured")
15941
16034
  }, 400);
15942
16035
  const modelOverride = original.modelName ? aiConfig.provider.models.find((m) => m.name === original.modelName) : void 0;
15943
16036
  if (original.modelName && !modelOverride) return c.json({
15944
16037
  success: false,
15945
- error: `Model "${original.modelName}" not found in AI settings`
16038
+ error: t("server.modelNotFound", { name: original.modelName })
15946
16039
  }, 400);
15947
16040
  const source = original.source.type === "file" && original.source.filePath ? {
15948
16041
  type: "file",
@@ -15981,7 +16074,7 @@ function extractRoutes(config) {
15981
16074
  const id = c.req.param("id");
15982
16075
  if (!await readExtractionAuditRecord(aiexDir, id)) return c.json({
15983
16076
  success: false,
15984
- error: "Extraction record not found"
16077
+ error: t("server.extractionRecordNotFound")
15985
16078
  }, 404);
15986
16079
  await deleteExtractionAuditRecord(aiexDir, id);
15987
16080
  return c.json({ success: true });
@@ -16010,16 +16103,16 @@ function schemaRoutes(config) {
16010
16103
  const jsonFiles = (await fs.readdir(schemaDir)).filter((f) => f.endsWith(".json"));
16011
16104
  return c.json(jsonFiles);
16012
16105
  });
16013
- app.get("/schema/:name", zValidator("param", schemaFileParamSchema, invalidParamResponse("Invalid schema file name")), async (c) => {
16106
+ app.get("/schema/:name", zValidator("param", schemaFileParamSchema, invalidParamResponse(t("server.invalidTableName"))), async (c) => {
16014
16107
  const { name: name$1 } = c.req.valid("param");
16015
16108
  const filePath = path.join(schemaDir, name$1);
16016
16109
  try {
16017
16110
  return c.json(await readFile(filePath));
16018
16111
  } catch {
16019
- return c.json({ error: "Schema not found" }, 404);
16112
+ return c.json({ error: t("server.schemaNotFound") }, 404);
16020
16113
  }
16021
16114
  });
16022
- app.post("/schema/:name", zValidator("param", schemaFileParamSchema, invalidParamResponse("Invalid schema file name")), async (c) => {
16115
+ app.post("/schema/:name", zValidator("param", schemaFileParamSchema, invalidParamResponse(t("server.invalidTableName"))), async (c) => {
16023
16116
  const { name: name$1 } = c.req.valid("param");
16024
16117
  const filePath = path.join(schemaDir, name$1);
16025
16118
  try {
@@ -16035,10 +16128,10 @@ function schemaRoutes(config) {
16035
16128
  } catch {}
16036
16129
  return c.json({ success: true });
16037
16130
  } catch {
16038
- return c.json({ error: "Failed to save schema" }, 500);
16131
+ return c.json({ error: t("server.saveSchemaFailed") }, 500);
16039
16132
  }
16040
16133
  });
16041
- app.get("/prompt-snapshot/:name", zValidator("param", tableNameParamSchema, invalidParamResponse("Invalid table name")), async (c) => {
16134
+ app.get("/prompt-snapshot/:name", zValidator("param", tableNameParamSchema, invalidParamResponse(t("server.invalidTableName"))), async (c) => {
16042
16135
  const { name: name$1 } = c.req.valid("param");
16043
16136
  const aiexDir = path.dirname(schemaDir);
16044
16137
  const snapshotPath = path.join(aiexDir, "extracted", `${name$1}.prompt.md`);
@@ -16051,11 +16144,11 @@ function schemaRoutes(config) {
16051
16144
  } catch {
16052
16145
  return c.json({
16053
16146
  success: false,
16054
- error: "Prompt snapshot not found. Save the schema first."
16147
+ error: t("server.promptSnapshotNotAvailable")
16055
16148
  }, 404);
16056
16149
  }
16057
16150
  });
16058
- app.delete("/schema/:name", zValidator("param", schemaFileParamSchema, invalidParamResponse("Invalid schema file name")), async (c) => {
16151
+ app.delete("/schema/:name", zValidator("param", schemaFileParamSchema, invalidParamResponse(t("server.invalidTableName"))), async (c) => {
16059
16152
  const { name: name$1 } = c.req.valid("param");
16060
16153
  const filePath = path.join(schemaDir, name$1);
16061
16154
  try {
@@ -16071,7 +16164,7 @@ function schemaRoutes(config) {
16071
16164
  await fs.unlink(filePath);
16072
16165
  return c.json({ success: true });
16073
16166
  } catch {
16074
- return c.json({ error: "Failed to delete schema" }, 500);
16167
+ return c.json({ error: t("server.deleteSchemaFailed") }, 500);
16075
16168
  }
16076
16169
  });
16077
16170
  app.post("/migrate", async (c) => {
@@ -16082,7 +16175,7 @@ function schemaRoutes(config) {
16082
16175
  const status = result.schemaCount === 0 ? 400 : 500;
16083
16176
  return c.json({
16084
16177
  success: false,
16085
- error: result.error || "Migration failed"
16178
+ error: result.error || t("server.migrationFailed")
16086
16179
  }, status);
16087
16180
  }
16088
16181
  return c.json({
@@ -16123,7 +16216,7 @@ function createApp(config, staticDir) {
16123
16216
  html = await fs.readFile(`${staticDir}/index.html`, "utf-8");
16124
16217
  } catch {
16125
16218
  html = `<!DOCTYPE html>
16126
- <html lang="zh-CN">
16219
+ <html lang="en">
16127
16220
  <head>
16128
16221
  <meta charset="UTF-8" />
16129
16222
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
@@ -16173,31 +16266,32 @@ async function startWebServer(input) {
16173
16266
  const webCommand = defineCommand({
16174
16267
  meta: {
16175
16268
  name: "web",
16176
- description: "Start visual JSON Schema editor"
16269
+ description: t("command.web.description")
16177
16270
  },
16178
16271
  args: { port: {
16179
16272
  type: "string",
16180
16273
  alias: "p",
16181
- description: "Port to listen on",
16274
+ description: t("command.web.args.port"),
16182
16275
  default: "13000"
16183
16276
  } },
16184
16277
  async run({ args }) {
16278
+ await initI18n();
16185
16279
  intro(pc.inverse(" aiex web "));
16186
16280
  const cwd = process.cwd();
16187
16281
  const port = Number(args.port) || 13e3;
16188
16282
  const config = createMigrationConfig(cwd);
16189
16283
  const s = spinner();
16190
- s.start("Starting web server...");
16284
+ s.start(t("command.web.starting"));
16191
16285
  await startWebServer({
16192
16286
  config,
16193
16287
  port,
16194
16288
  onStarted(info) {
16195
- s.stop(`Server running at ${pc.cyan(info.url)}`);
16196
- consola.info(`Schema directory: ${pc.dim(info.schemaPath)}`);
16197
- consola.info("Press Ctrl+C to stop");
16289
+ s.stop(t("command.web.serverRunning", { url: pc.cyan(info.url) }));
16290
+ consola.info(t("command.web.schemaDir", { path: pc.dim(info.schemaPath) }));
16291
+ consola.info(t("command.web.pressCtrlC"));
16198
16292
  },
16199
16293
  onOpenFailed(url) {
16200
- consola.warn(`Could not open browser. Visit ${url} manually.`);
16294
+ consola.warn(t("command.web.browserOpenFailed", { url }));
16201
16295
  }
16202
16296
  });
16203
16297
  }
@@ -16217,6 +16311,7 @@ const subCommands = {
16217
16311
 
16218
16312
  //#endregion
16219
16313
  //#region src/cli.ts
16314
+ await initI18n();
16220
16315
  seedConfig(createConfig());
16221
16316
  updateNotifier({ pkg: package_default }).notify();
16222
16317
  process.on("uncaughtException", (error) => {
@@ -16229,7 +16324,7 @@ process.on("unhandledRejection", (reason) => {
16229
16324
  process.exit(1);
16230
16325
  });
16231
16326
  if (process.argv[2] === "_complete") {
16232
- const { getCompletions } = await import("./completions-C3rmTwXZ.mjs");
16327
+ const { getCompletions } = await import("./completions-Bh0DOngr.mjs");
16233
16328
  const suggestions = getCompletions(subCommands, process.argv.slice(4));
16234
16329
  for (const s of suggestions) process.stdout.write(`${s}\n`);
16235
16330
  process.exit(0);