aiex-cli 0.0.1-beta.29 → 0.0.1-beta.30

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 (108) hide show
  1. package/README.md +4 -15
  2. package/dist/cli.mjs +95 -246
  3. package/dist/{completions-ygS1okck.mjs → completions-C3rmTwXZ.mjs} +2 -2
  4. package/dist/core/schema-sqlite/migrate-helper.mjs +12 -7
  5. package/dist/{doctor-collector-j2dG7dG1.mjs → doctor-collector-DhiJrVw5.mjs} +14 -6
  6. package/dist/index.mjs +1 -1
  7. package/dist/web/assets/{AISettings-Dn58ZHhM.js → AISettings-BjXxD5Ts.js} +1 -1
  8. package/dist/web/assets/DataBrowser-CwcTG80-.js +6 -0
  9. package/dist/web/assets/{ExtractionViewer-CTS1RXzc.js → ExtractionViewer-CsdK1kKK.js} +1 -1
  10. package/dist/web/assets/{JsonSchemaEditor-Dpgu6HPz.js → JsonSchemaEditor-BhY9BNOn.js} +15 -15
  11. package/dist/web/assets/{cssMode-BM5FOYIl.js → cssMode-CPThwItX.js} +1 -1
  12. package/dist/web/assets/dist-9yHVMqQ0.js +1 -0
  13. package/dist/web/assets/{editor.main-C2Q97Dkk.js → editor.main-BnOkwRFv.js} +2 -2
  14. package/dist/web/assets/{freemarker2-BqyJTCTn.js → freemarker2-DWDTYVJR.js} +1 -1
  15. package/dist/web/assets/{handlebars-DxRJTefg.js → handlebars-D4DzjGQ7.js} +1 -1
  16. package/dist/web/assets/{html-gyvgrapw.js → html-DnzhKSoD.js} +1 -1
  17. package/dist/web/assets/{htmlMode-CNjCRwdY.js → htmlMode-CR7UKfEH.js} +1 -1
  18. package/dist/web/assets/index-DCAKotsV.js +882 -0
  19. package/dist/web/assets/{javascript-BK6ufvq6.js → javascript-D2srszZ8.js} +1 -1
  20. package/dist/web/assets/{jsonMode-m2trGjkO.js → jsonMode-B4jaPYEr.js} +1 -1
  21. package/dist/web/assets/{liquid-BtyuYqQQ.js → liquid-CIT2Wl_l.js} +1 -1
  22. package/dist/web/assets/{mdx-C8K4EvCQ.js → mdx-CWLaEOFy.js} +1 -1
  23. package/dist/web/assets/{monaco.contribution-BTr-G8hO.js → monaco.contribution-DDv5ldfS.js} +2 -2
  24. package/dist/web/assets/{python-8dyH1nS_.js → python-6CGfpCNq.js} +1 -1
  25. package/dist/web/assets/{razor-DtWMI74k.js → razor-DEMMh3TD.js} +1 -1
  26. package/dist/web/assets/{tsMode-Dv8YG-YK.js → tsMode-Cm1NtjPs.js} +1 -1
  27. package/dist/web/assets/{typescript-DbClKYS3.js → typescript-BM9aPEFg.js} +1 -1
  28. package/dist/web/assets/{xml-Bb59gjP6.js → xml-CoSbvcg5.js} +1 -1
  29. package/dist/web/assets/{yaml-DVMb_IfV.js → yaml-56GOgy8k.js} +1 -1
  30. package/dist/web/index.html +4 -3
  31. package/package.json +8 -3
  32. package/src/core/schema-sqlite/migrate-helper.ts +6 -5
  33. package/dist/web/assets/DataBrowser-B6WECCZM.js +0 -6
  34. package/dist/web/assets/index-g2pWXPQZ.js +0 -882
  35. /package/dist/web/assets/{abap-DiwvWnMr.js → abap-Bgec7Keq.js} +0 -0
  36. /package/dist/web/assets/{apex-CmtZjKlf.js → apex-VBlPwEoQ.js} +0 -0
  37. /package/dist/web/assets/{api-client-CbQEkaKT.js → api-client-D2Y_-4JM.js} +0 -0
  38. /package/dist/web/assets/{azcli-DL2My_i-.js → azcli-DKqrEFBx.js} +0 -0
  39. /package/dist/web/assets/{bat-B-nC98wG.js → bat-DdgQWy_0.js} +0 -0
  40. /package/dist/web/assets/{bicep-Ju5MwOgh.js → bicep-CRMM43EB.js} +0 -0
  41. /package/dist/web/assets/{cameligo-8Eu1TyBr.js → cameligo-UatALtML.js} +0 -0
  42. /package/dist/web/assets/{clojure-u-RpMkH3.js → clojure-D8JU08RA.js} +0 -0
  43. /package/dist/web/assets/{coffee-CdA7bbTe.js → coffee-C56wu358.js} +0 -0
  44. /package/dist/web/assets/{cpp-CzNFP8ks.js → cpp-CyZLvhJG.js} +0 -0
  45. /package/dist/web/assets/{csharp-j1LThmcE.js → csharp-BJl3ixva.js} +0 -0
  46. /package/dist/web/assets/{csp-CLRC61y6.js → csp-CxEKxmO-.js} +0 -0
  47. /package/dist/web/assets/{css-r6rC_7P2.js → css-B0t_muXd.js} +0 -0
  48. /package/dist/web/assets/{cypher-CW08XVUh.js → cypher-D1hqiMFD.js} +0 -0
  49. /package/dist/web/assets/{dart-Cs9aL5T_.js → dart-Bz550Pyv.js} +0 -0
  50. /package/dist/web/assets/{dockerfile-BWM0M184.js → dockerfile-CIXgVAuA.js} +0 -0
  51. /package/dist/web/assets/{ecl-MJJuer5P.js → ecl-D9qbvZoA.js} +0 -0
  52. /package/dist/web/assets/{editor.api-nsOUOZde.js → editor.api-C8BHpRhn.js} +0 -0
  53. /package/dist/web/assets/{elixir-D2AIuXqn.js → elixir-b2M38fAy.js} +0 -0
  54. /package/dist/web/assets/{flow9-B2H24giC.js → flow9-Dq1UYMkt.js} +0 -0
  55. /package/dist/web/assets/{fsharp-CFNadkg7.js → fsharp-BaeLhgfq.js} +0 -0
  56. /package/dist/web/assets/{go-dSur1iB2.js → go-Bd-NFKIC.js} +0 -0
  57. /package/dist/web/assets/{graphql-qyhAo11d.js → graphql-DZVerJfy.js} +0 -0
  58. /package/dist/web/assets/{hcl-DFzjMyzm.js → hcl-CAVzrZfH.js} +0 -0
  59. /package/dist/web/assets/{ini-TdzA8TIl.js → ini-CyXdX58t.js} +0 -0
  60. /package/dist/web/assets/{java-CSGA9pkE.js → java-B5pNgvhy.js} +0 -0
  61. /package/dist/web/assets/{julia-9izz5OsY.js → julia-XRhmV3AN.js} +0 -0
  62. /package/dist/web/assets/{kotlin-DuPK7AtF.js → kotlin-DOd3J5vr.js} +0 -0
  63. /package/dist/web/assets/{less-B8d93iCg.js → less-veZSnyw6.js} +0 -0
  64. /package/dist/web/assets/{lexon-DWtEIyu7.js → lexon-QWGkuK0H.js} +0 -0
  65. /package/dist/web/assets/{lua-Ciq0OGgt.js → lua-CYGpjuO5.js} +0 -0
  66. /package/dist/web/assets/{m3-Cki6JWj_.js → m3-yNnrZkdc.js} +0 -0
  67. /package/dist/web/assets/{markdown-Cu47xwU0.js → markdown-BCSWEPSX.js} +0 -0
  68. /package/dist/web/assets/{mips-BM8ui995.js → mips-OpYmcC30.js} +0 -0
  69. /package/dist/web/assets/{msdax-DqLio0_c.js → msdax-2oxoTO9Z.js} +0 -0
  70. /package/dist/web/assets/{mysql-v1wbjJOq.js → mysql-5KlC-K_9.js} +0 -0
  71. /package/dist/web/assets/{objective-c-CQl3PGSB.js → objective-c-CcDCgtLx.js} +0 -0
  72. /package/dist/web/assets/{pascal-D4iW0ZtD.js → pascal-BZGsbaEV.js} +0 -0
  73. /package/dist/web/assets/{pascaligo-BdC9CZdj.js → pascaligo-DtD5qU3G.js} +0 -0
  74. /package/dist/web/assets/{perl-BL10m4XD.js → perl-C1jNNS3E.js} +0 -0
  75. /package/dist/web/assets/{pgsql-Be_oqVo3.js → pgsql-CT0fhiZa.js} +0 -0
  76. /package/dist/web/assets/{php-BtvXSFRI.js → php-D6DrXoPM.js} +0 -0
  77. /package/dist/web/assets/{pla-B2vUy15C.js → pla-b3-HN2pF.js} +0 -0
  78. /package/dist/web/assets/{postiats-CbmTTfXr.js → postiats-Bin2ApVS.js} +0 -0
  79. /package/dist/web/assets/{powerquery-DszLhJGx.js → powerquery-7ASnn-ZG.js} +0 -0
  80. /package/dist/web/assets/{powershell-B0dYktF6.js → powershell-t4p7sU1H.js} +0 -0
  81. /package/dist/web/assets/{preload-helper-DWTEM3RW.js → preload-helper-Dd-HcVz_.js} +0 -0
  82. /package/dist/web/assets/{protobuf-CZvaj1VX.js → protobuf-BUGeWa_j.js} +0 -0
  83. /package/dist/web/assets/{pug-CPDx1B3S.js → pug-BuKcgC9s.js} +0 -0
  84. /package/dist/web/assets/{qsharp-CAxMZVjw.js → qsharp-DxLLX8mo.js} +0 -0
  85. /package/dist/web/assets/{r-8DbbFX2l.js → r-DMlFgn7A.js} +0 -0
  86. /package/dist/web/assets/{redis-DRWj9MtJ.js → redis-cXItkC5u.js} +0 -0
  87. /package/dist/web/assets/{redshift-C6cElE_5.js → redshift-BZVbW7HE.js} +0 -0
  88. /package/dist/web/assets/{restructuredtext-W9pS9n3m.js → restructuredtext-BzjxwS8h.js} +0 -0
  89. /package/dist/web/assets/{ruby-BKnzWnk-.js → ruby-C5nyLV4l.js} +0 -0
  90. /package/dist/web/assets/{rust-YPCclWwe.js → rust-BcmMsHdf.js} +0 -0
  91. /package/dist/web/assets/{sb-BgM4DTFb.js → sb-Dnb1iy6B.js} +0 -0
  92. /package/dist/web/assets/{scala-fz1OPLMl.js → scala-anMIFYpA.js} +0 -0
  93. /package/dist/web/assets/{scheme-8Uz1RIbu.js → scheme-BItQTe08.js} +0 -0
  94. /package/dist/web/assets/{scss-Djo3IYXr.js → scss-BOv51BJ5.js} +0 -0
  95. /package/dist/web/assets/{select-DyjIzt-v.js → select-BGex2SPs.js} +0 -0
  96. /package/dist/web/assets/{shell-CINF5Tx_.js → shell-BsRYRTNN.js} +0 -0
  97. /package/dist/web/assets/{solidity-GgiNEuUm.js → solidity-BtuLgGDx.js} +0 -0
  98. /package/dist/web/assets/{sophia-Culj97P9.js → sophia-B0Vkc5MF.js} +0 -0
  99. /package/dist/web/assets/{sparql-C2ZlpxOY.js → sparql-B7lvkZQM.js} +0 -0
  100. /package/dist/web/assets/{sql-BEf5Pg7Y.js → sql-DvP5MpA3.js} +0 -0
  101. /package/dist/web/assets/{st-CT6UUoeH.js → st-GVUeyB3U.js} +0 -0
  102. /package/dist/web/assets/{swift-B5g0xTG3.js → swift-DSPIoCjm.js} +0 -0
  103. /package/dist/web/assets/{systemverilog-CEgQz9DR.js → systemverilog-Icj2-k23.js} +0 -0
  104. /package/dist/web/assets/{tcl-D0qL2L0I.js → tcl-Cd8KQcm-.js} +0 -0
  105. /package/dist/web/assets/{twig-BFUAVf1E.js → twig-CBHmt8z3.js} +0 -0
  106. /package/dist/web/assets/{typespec-CjVVcNKm.js → typespec-Ckc037mq.js} +0 -0
  107. /package/dist/web/assets/{vb-CZJr-DQz.js → vb-B97GW9Wb.js} +0 -0
  108. /package/dist/web/assets/{wgsl-ivoXUo2e.js → wgsl-DIKmb3YH.js} +0 -0
package/README.md CHANGED
@@ -21,7 +21,7 @@ npm install -g aiex-cli
21
21
  ```
22
22
 
23
23
  ```bash
24
- aiex schema --init # set up .aiex/schema/ directory
24
+ aiex web # configure schemas and AI settings in the browser
25
25
  aiex schema # generate SQLite from JSON Schema files
26
26
  aiex extract -s invoice -f invoice.pdf # extract data with AI and insert into database
27
27
  ```
@@ -41,17 +41,7 @@ aiex extract -s invoice -f invoice.pdf # extract data with AI and insert into d
41
41
 
42
42
  ## 🚀 Getting Started
43
43
 
44
- ### 1. Initialize
45
-
46
- ```bash
47
- aiex schema --init
48
- ```
49
-
50
- Creates a `.aiex/` directory with example schemas to get you started.
51
-
52
- Add your own JSON Schema files to `.aiex/schema/` (one file per table), then run `aiex schema` to migrate them into the database.
53
-
54
- ### 2. Visual Editor
44
+ ### 1. Configure In Web UI
55
45
 
56
46
  ```bash
57
47
  aiex web
@@ -59,7 +49,7 @@ aiex web
59
49
 
60
50
  Opens a browser UI where you can visually design and manage your schemas, configure AI settings, preview extraction prompts, and apply changes to the database.
61
51
 
62
- ### 3. Generate Database
52
+ ### 2. Generate Database
63
53
 
64
54
  ```bash
65
55
  aiex schema
@@ -67,7 +57,7 @@ aiex schema
67
57
 
68
58
  Converts your JSON Schema files into a SQLite database with full migration support.
69
59
 
70
- ### 4. Extract Data
60
+ ### 3. Extract Data
71
61
 
72
62
  ```bash
73
63
  aiex extract # interactive mode (prompts for schema & input)
@@ -97,7 +87,6 @@ By default, aiex automatically selects a model based on your input type (vision-
97
87
 
98
88
  | Command | Description |
99
89
  | --- | --- |
100
- | `aiex schema --init` | Scaffold `.aiex/` directory with example schemas |
101
90
  | `aiex schema` | Parse JSON Schema files and migrate to SQLite |
102
91
  | `aiex schema --generate` | Generate Drizzle schema code only (skip migration) |
103
92
  | `aiex web` | Launch visual schema editor in browser |
package/dist/cli.mjs CHANGED
@@ -1,10 +1,11 @@
1
- import { C as doctorDiagnosticsTableRows, _ as seedConfig, a as parseJsonSchema, b as package_default, c as getDefaultAIConfig, d as DEFAULT_MINERU_CONFIG, f as DEFAULT_PROMPT_CONFIG, g as createConfig, h as AIConfigSchema, i as JsonSchemaDefinitionSchema, l as readAIConfig, m as PLACEHOLDER_TEXT, n as createMigrationConfig, o as toSnakeCase, p as PLACEHOLDER_SCHEMA, s as generateDrizzleSchema, t as collectDoctorDiagnostics, u as writeAIConfig, v as description, w as formatDoctorDiagnosticsJson, x as version, y as name } from "./doctor-collector-j2dG7dG1.mjs";
1
+ import { C as doctorDiagnosticsTableRows, _ as seedConfig, a as parseJsonSchema, b as package_default, c as getDefaultAIConfig, d as DEFAULT_MINERU_CONFIG, f as DEFAULT_PROMPT_CONFIG, g as createConfig, h as AIConfigSchema, i as JsonSchemaDefinitionSchema, l as readAIConfig, m as PLACEHOLDER_TEXT, n as createMigrationConfig, o as toSnakeCase, p as PLACEHOLDER_SCHEMA, s as generateDrizzleSchema, t as collectDoctorDiagnostics, u as writeAIConfig, v as description, w as formatDoctorDiagnosticsJson, x as version, y as name } from "./doctor-collector-DhiJrVw5.mjs";
2
2
  import { createRequire } from "node:module";
3
3
  import fs from "node:fs/promises";
4
4
  import os from "node:os";
5
5
  import path from "node:path";
6
6
  import process from "node:process";
7
- import { ZodError } from "zod";
7
+ import { readFile, writeFile } from "jsonfile";
8
+ import { ZodError, z } from "zod";
8
9
  import { fileURLToPath } from "node:url";
9
10
  import { defineCommand, runMain } from "citty";
10
11
  import { consola } from "consola";
@@ -16,19 +17,23 @@ import { createOpenAICompatible } from "@ai-sdk/openai-compatible";
16
17
  import { LangfuseSpanProcessor } from "@langfuse/otel";
17
18
  import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";
18
19
  import { APICallError, Output, generateText, jsonSchema } from "ai";
20
+ import mime from "mime";
19
21
  import pRetry from "p-retry";
22
+ import { jsonrepair } from "jsonrepair";
20
23
  import fs$1 from "node:fs";
21
24
  import Database from "better-sqlite3";
22
- import picomatch from "picomatch";
25
+ import { glob, globSync } from "tinyglobby";
23
26
  import { execa } from "execa";
24
27
  import { Buffer } from "node:buffer";
25
28
  import { extractText, getMeta } from "unpdf";
26
29
  import { execFile } from "node:child_process";
27
30
  import { promisify } from "node:util";
28
31
  import { serve } from "@hono/node-server";
32
+ import open from "open";
29
33
  import { serveStatic } from "@hono/node-server/serve-static";
30
34
  import { Hono } from "hono";
31
35
  import { cors } from "hono/cors";
36
+ import { zValidator } from "@hono/zod-validator";
32
37
 
33
38
  //#region src/core/schema-sqlite/helpers.ts
34
39
  const __filename = fileURLToPath(import.meta.url);
@@ -12806,6 +12811,15 @@ async function withRetry(fn, onRetry, maxRetries = 5) {
12806
12811
 
12807
12812
  //#endregion
12808
12813
  //#region src/core/ai-extraction/json-utils.ts
12814
+ function parseJsonLike(text$1) {
12815
+ const trimmed = text$1.trim();
12816
+ try {
12817
+ return JSON.parse(trimmed);
12818
+ } catch {
12819
+ if (!trimmed.startsWith("{") && !trimmed.startsWith("[")) throw new SyntaxError("JSON candidate must start with an object or array");
12820
+ return JSON.parse(jsonrepair(trimmed));
12821
+ }
12822
+ }
12809
12823
  function stripFences(text$1) {
12810
12824
  const trimmed = text$1.trim();
12811
12825
  if (!trimmed.startsWith("```")) return null;
@@ -12835,11 +12849,11 @@ function safeParseJSON(text$1) {
12835
12849
  } catch {}
12836
12850
  const fromFence = stripFences(cleaned);
12837
12851
  if (fromFence) try {
12838
- return JSON.parse(fromFence);
12852
+ return parseJsonLike(fromFence);
12839
12853
  } catch {}
12840
12854
  const extracted = extractFirstJSON(cleaned);
12841
12855
  if (extracted) try {
12842
- return JSON.parse(extracted);
12856
+ return parseJsonLike(extracted);
12843
12857
  } catch {}
12844
12858
  const truncated = text$1.length > 200 ? `${text$1.slice(0, 200)}...` : text$1;
12845
12859
  throw new Error(`Failed to parse JSON from model output. Expected a valid JSON object or array but received unparseable text. Raw output: ${truncated}`);
@@ -12974,37 +12988,22 @@ function initLangfuse(config) {
12974
12988
  }
12975
12989
  const SYSTEM_PROMPT_REGEX = /## System Prompt\n([\s\S]*?)(?=## User Prompt|$)/;
12976
12990
  const USER_PROMPT_REGEX = /## User Prompt Template\n([\s\S]*)$/;
12977
- const MIME_TYPES = {
12978
- png: "image/png",
12979
- jpg: "image/jpeg",
12980
- jpeg: "image/jpeg",
12981
- gif: "image/gif",
12982
- webp: "image/webp",
12983
- bmp: "image/bmp",
12984
- svg: "image/svg+xml",
12985
- pdf: "application/pdf",
12986
- txt: "text/plain",
12987
- csv: "text/csv",
12988
- json: "application/json",
12989
- md: "text/markdown",
12990
- html: "text/html"
12991
- };
12992
12991
  function detectMimeType(filePath) {
12993
- return MIME_TYPES[path.extname(filePath).toLowerCase().replace(".", "")] || "application/octet-stream";
12992
+ return mime.getType(filePath) ?? "application/octet-stream";
12994
12993
  }
12995
12994
  async function readFilePart(filePath) {
12996
- const mime = detectMimeType(filePath);
12995
+ const mime$1 = detectMimeType(filePath);
12997
12996
  const buffer = await fs.readFile(filePath);
12998
12997
  const name$1 = path.basename(filePath);
12999
- if (mime.startsWith("image/")) return {
12998
+ if (mime$1.startsWith("image/")) return {
13000
12999
  type: "image",
13001
13000
  image: buffer,
13002
- mimeType: mime
13001
+ mimeType: mime$1
13003
13002
  };
13004
13003
  return {
13005
13004
  type: "file",
13006
13005
  data: buffer,
13007
- mediaType: mime,
13006
+ mediaType: mime$1,
13008
13007
  filename: name$1
13009
13008
  };
13010
13009
  }
@@ -13210,7 +13209,10 @@ async function extractStructuredData(input) {
13210
13209
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
13211
13210
  const outputFileName = `${schema.table.name}-${timestamp}.json`;
13212
13211
  const outputPath = path.join(outputDir, outputFileName);
13213
- await fs.writeFile(outputPath, `${JSON.stringify(data, null, 2)}\n`);
13212
+ await writeFile(outputPath, data, {
13213
+ spaces: 2,
13214
+ EOL: "\n"
13215
+ });
13214
13216
  return {
13215
13217
  success: true,
13216
13218
  outputPath,
@@ -13395,17 +13397,11 @@ async function pathExists(filePath) {
13395
13397
  }
13396
13398
  }
13397
13399
  async function collectMarkdownFiles(dir) {
13398
- const entries = await fs.readdir(dir, { withFileTypes: true });
13399
- const files = [];
13400
- for (const entry of entries) {
13401
- const entryPath = path.join(dir, entry.name);
13402
- if (entry.isDirectory()) {
13403
- files.push(...await collectMarkdownFiles(entryPath));
13404
- continue;
13405
- }
13406
- if (entry.isFile() && entry.name.toLowerCase().endsWith(".md")) files.push(entryPath);
13407
- }
13408
- return files.sort();
13400
+ return (await glob("**/*.md", {
13401
+ cwd: dir,
13402
+ absolute: true,
13403
+ onlyFiles: true
13404
+ })).sort();
13409
13405
  }
13410
13406
  async function selectMarkdownFile(outputDir, basename) {
13411
13407
  const files = await collectMarkdownFiles(outputDir);
@@ -13551,6 +13547,7 @@ const SUPPORTED_EXTENSIONS = new Set([
13551
13547
  "yml"
13552
13548
  ]);
13553
13549
  const JSON_EXT_RE = /\.json$/;
13550
+ const SUPPORTED_FILE_PATTERN = `*.{${[...SUPPORTED_EXTENSIONS].join(",")}}`;
13554
13551
  async function ensureDatabaseReady(dbPath, schema) {
13555
13552
  try {
13556
13553
  await fs.access(dbPath);
@@ -13571,22 +13568,20 @@ async function ensureDatabaseReady(dbPath, schema) {
13571
13568
  return null;
13572
13569
  }
13573
13570
  function listSupportedFiles(dir, pattern) {
13574
- const entries = fs$1.readdirSync(dir, { withFileTypes: true });
13575
- const files = [];
13576
- for (const entry of entries) {
13577
- if (entry.isDirectory()) continue;
13578
- const ext = path.extname(entry.name).toLowerCase().replace(".", "");
13579
- if (!SUPPORTED_EXTENSIONS.has(ext)) continue;
13580
- if (pattern && !picomatch.isMatch(entry.name, pattern)) continue;
13581
- files.push(path.join(dir, entry.name));
13582
- }
13583
- return files.sort();
13571
+ if (!fs$1.statSync(dir).isDirectory()) throw new Error(`Not a directory: ${dir}`);
13572
+ return globSync(pattern ?? SUPPORTED_FILE_PATTERN, {
13573
+ cwd: dir,
13574
+ absolute: true,
13575
+ onlyFiles: true
13576
+ }).filter((file) => {
13577
+ const ext = path.extname(file).toLowerCase().replace(".", "");
13578
+ return SUPPORTED_EXTENSIONS.has(ext);
13579
+ }).sort();
13584
13580
  }
13585
13581
  async function loadSchema(config, schemaName) {
13586
13582
  const schemaPath = path.join(config.schemaPath, `${schemaName}.json`);
13587
13583
  try {
13588
- const content = await fs.readFile(schemaPath, "utf-8");
13589
- const parsed = JSON.parse(content);
13584
+ const parsed = await readFile(schemaPath);
13590
13585
  return { schema: JsonSchemaDefinitionSchema.parse(parsed) };
13591
13586
  } catch (e) {
13592
13587
  if (e instanceof ZodError) return {
@@ -13882,7 +13877,7 @@ const extractCommand = defineCommand({
13882
13877
  async function runInteractive(aiexDir, config, aiConfig, modelOverride) {
13883
13878
  const schemas = await listSchemas(aiexDir);
13884
13879
  if (schemas.length === 0) {
13885
- failCommand(`No schema files found in ${pc.cyan(".aiex/schema/")}. Run ${pc.cyan("aiex schema --init")} first, or add JSON Schema files.`);
13880
+ failCommand(`No schema files found in ${pc.cyan(".aiex/schema/")}. Run ${pc.cyan("aiex web")} to create and configure schemas first.`);
13886
13881
  return false;
13887
13882
  }
13888
13883
  const schemaName = await select({
@@ -13974,131 +13969,9 @@ function cancel(msg) {
13974
13969
  process.exitCode = 0;
13975
13970
  }
13976
13971
 
13977
- //#endregion
13978
- //#region schemas/table-schema.json
13979
- var $id = "https://raw.githubusercontent.com/OSpoon/aiex-cli/main/app/cli/schemas/table-schema.json";
13980
-
13981
13972
  //#endregion
13982
13973
  //#region src/core/schema-runner.ts
13983
- const execFileAsync$1 = promisify(execFile);
13984
- const EXAMPLE_SCORE_REPORT_SCHEMA = {
13985
- $schema: $id,
13986
- title: "ScoreReport",
13987
- type: "object",
13988
- table: {
13989
- name: "score_report",
13990
- timestamps: true
13991
- },
13992
- properties: {
13993
- name: {
13994
- type: "string",
13995
- description: "姓名"
13996
- },
13997
- reportNumber: {
13998
- type: "string",
13999
- description: "报告编号"
14000
- },
14001
- gender: {
14002
- type: "string",
14003
- description: "性别"
14004
- },
14005
- printDate: {
14006
- type: "string",
14007
- format: "date-time",
14008
- description: "打印日期"
14009
- },
14010
- examYear: {
14011
- type: "integer",
14012
- description: "考试年份"
14013
- },
14014
- examType: {
14015
- type: "string",
14016
- description: "考试类型,如全国统考"
14017
- },
14018
- examCategory: {
14019
- type: "string",
14020
- description: "考试类别,如普通高考"
14021
- },
14022
- province: {
14023
- type: "string",
14024
- description: "考试省份"
14025
- },
14026
- subjectCategory: {
14027
- type: "string",
14028
- description: "科类,如艺术(文)"
14029
- },
14030
- chinese: {
14031
- type: "integer",
14032
- description: "语文成绩"
14033
- },
14034
- chineseFull: {
14035
- type: "integer",
14036
- description: "语文满分"
14037
- },
14038
- math: {
14039
- type: "integer",
14040
- description: "数学成绩"
14041
- },
14042
- mathFull: {
14043
- type: "integer",
14044
- description: "数学满分"
14045
- },
14046
- foreignLang: {
14047
- type: "integer",
14048
- description: "外语成绩"
14049
- },
14050
- foreignLangFull: {
14051
- type: "integer",
14052
- description: "外语满分"
14053
- },
14054
- comprehensive: {
14055
- type: "integer",
14056
- description: "综合成绩"
14057
- },
14058
- comprehensiveFull: {
14059
- type: "integer",
14060
- description: "综合满分"
14061
- },
14062
- totalScore: {
14063
- type: "integer",
14064
- description: "总分"
14065
- },
14066
- totalFullScore: {
14067
- type: "integer",
14068
- description: "总分满分"
14069
- },
14070
- batchLineFirst: {
14071
- type: "integer",
14072
- description: "本科第一批录取分数线"
14073
- },
14074
- batchLineSecond: {
14075
- type: "integer",
14076
- description: "本科第二批录取分数线"
14077
- }
14078
- },
14079
- required: [
14080
- "name",
14081
- "examYear",
14082
- "examType",
14083
- "province",
14084
- "totalScore"
14085
- ]
14086
- };
14087
- async function writeJsonIfAbsent(filePath, data) {
14088
- try {
14089
- await fs.writeFile(filePath, `${JSON.stringify(data, null, 2)}\n`, { flag: "wx" });
14090
- return "created";
14091
- } catch (error) {
14092
- if (error.code === "EEXIST") return "skipped";
14093
- throw error;
14094
- }
14095
- }
14096
- async function initSchemaProject(config) {
14097
- await fs.mkdir(config.schemaPath, { recursive: true });
14098
- await fs.mkdir(path.dirname(config.drizzleSchemaPath), { recursive: true });
14099
- await fs.mkdir(config.migrationsPath, { recursive: true });
14100
- return { scoreReportStatus: await writeJsonIfAbsent(path.join(config.schemaPath, "score_report.json"), EXAMPLE_SCORE_REPORT_SCHEMA) };
14101
- }
13974
+ const execFileAsync = promisify(execFile);
14102
13975
  async function listSchemaFiles(schemaDir) {
14103
13976
  try {
14104
13977
  return (await fs.readdir(schemaDir)).filter((f) => f.endsWith(".json")).map((f) => path.join(schemaDir, f)).sort();
@@ -14163,7 +14036,7 @@ async function runSchemaMigration(config, migrationName) {
14163
14036
  ];
14164
14037
  if (migrationName) helperArgs.push(migrationName);
14165
14038
  try {
14166
- const { stdout, stderr } = await execFileAsync$1(process.execPath, helperArgs, { cwd: process.cwd() });
14039
+ const { stdout, stderr } = await execFileAsync(process.execPath, helperArgs, { cwd: process.cwd() });
14167
14040
  return parseMigrationOutput(stdout, stderr);
14168
14041
  } catch (error) {
14169
14042
  const execError = error;
@@ -14219,12 +14092,6 @@ const schemaCommand = defineCommand({
14219
14092
  description: "Sync JSON Schema to SQLite database"
14220
14093
  },
14221
14094
  args: {
14222
- init: {
14223
- type: "boolean",
14224
- alias: "i",
14225
- description: "Only initialize .aiex/ directory with example schema",
14226
- default: false
14227
- },
14228
14095
  generate: {
14229
14096
  type: "boolean",
14230
14097
  alias: "g",
@@ -14239,17 +14106,9 @@ const schemaCommand = defineCommand({
14239
14106
  async run({ args }) {
14240
14107
  intro(pc.inverse(" aiex schema "));
14241
14108
  const config = createMigrationConfig(process.cwd());
14242
- if (args.init) {
14243
- const initResult = await initSchemaProject(config);
14244
- consola.success(`Initialized ${pc.cyan(".aiex/")} with example schemas`);
14245
- if (initResult.scoreReportStatus === "skipped") consola.warn(`${pc.cyan(".aiex/schema/score_report.json")} already exists, skipped`);
14246
- consola.info("Example includes: ScoreReport (college entrance exam score report)");
14247
- outro("Run: aiex schema");
14248
- return;
14249
- }
14250
14109
  const schemaFiles = await listSchemaFiles(config.schemaPath);
14251
14110
  if (schemaFiles.length === 0) {
14252
- consola.info("Use --init to initialize with an example schema");
14111
+ consola.info(`Run ${pc.cyan("aiex web")} to create and configure schemas in the Web UI`);
14253
14112
  failCommand(`No schema files found in ${pc.cyan(".aiex/schema/")}`);
14254
14113
  return;
14255
14114
  }
@@ -14348,10 +14207,22 @@ function aiRoutes(config) {
14348
14207
  //#endregion
14349
14208
  //#region src/server/routes/data.ts
14350
14209
  const FILE_REGEX = /\.json$/;
14351
- const EXTRACTION_FILE_RE = /^[\w.-]+\.json$/;
14352
- const TABLE_NAME_RE$1 = /^[a-z][a-z0-9_]*$/;
14353
14210
  const TIMESTAMP_CLEANUP = /(\d{2})-(\d{2})-(\d{2})/;
14354
14211
  const TIMESTAMP_TZ = /(\d{3})Z/;
14212
+ const tableParamSchema = z.object({ name: z.string().regex(/^[a-z][a-z0-9_]*$/) });
14213
+ const extractionFileParamSchema = z.object({ name: z.string().regex(/^[\w.-]+\.json$/).refine((name$1) => name$1 === path.basename(name$1) && !name$1.includes("..")) });
14214
+ const tableQuerySchema = z.object({
14215
+ page: z.coerce.number().int().min(1).catch(1),
14216
+ pageSize: z.coerce.number().int().min(1).max(500).catch(50),
14217
+ search: z.string().catch(""),
14218
+ sortField: z.string().optional(),
14219
+ sortOrder: z.preprocess((value) => typeof value === "string" ? value.toLowerCase() : value, z.enum(["asc", "desc"]).catch("asc"))
14220
+ });
14221
+ function invalidParamResponse$1(message) {
14222
+ return (result, c) => {
14223
+ if (!result.success) return c.json({ error: message }, 400);
14224
+ };
14225
+ }
14355
14226
  function dataRoutes(config) {
14356
14227
  const app = new Hono();
14357
14228
  const aiexDir = path.dirname(config.schemaPath);
@@ -14406,8 +14277,7 @@ function dataRoutes(config) {
14406
14277
  }
14407
14278
  const tables = [];
14408
14279
  for (const file of schemaFiles) try {
14409
- const content = await fs.readFile(path.join(schemaDir, file), "utf-8");
14410
- const schema = JSON.parse(content);
14280
+ const schema = await readFile(path.join(schemaDir, file));
14411
14281
  const tableName = schema.table?.name;
14412
14282
  if (!tableName) continue;
14413
14283
  tables.push({
@@ -14423,14 +14293,9 @@ function dataRoutes(config) {
14423
14293
  return c.json({ error: error instanceof Error ? error.message : String(error) }, 500);
14424
14294
  }
14425
14295
  });
14426
- app.get("/data/tables/:name", async (c) => {
14427
- const tableName = c.req.param("name");
14428
- if (!TABLE_NAME_RE$1.test(tableName)) return c.json({ error: "Invalid table name" }, 400);
14429
- const sortField = c.req.query("sortField");
14430
- const sortOrder = c.req.query("sortOrder") || "asc";
14431
- const page = Math.max(1, Number.parseInt(c.req.query("page") || "1", 10) || 1);
14432
- const pageSize = Math.min(500, Math.max(1, Number.parseInt(c.req.query("pageSize") || "50", 10) || 50));
14433
- const search = c.req.query("search") || "";
14296
+ app.get("/data/tables/:name", zValidator("param", tableParamSchema, invalidParamResponse$1("Invalid table name")), zValidator("query", tableQuerySchema), async (c) => {
14297
+ const { name: tableName } = c.req.valid("param");
14298
+ const { page, pageSize, search, sortField, sortOrder } = c.req.valid("query");
14434
14299
  let db;
14435
14300
  try {
14436
14301
  db = new Database(config.databasePath, { readonly: true });
@@ -14449,7 +14314,7 @@ function dataRoutes(config) {
14449
14314
  pk: !!col.pk
14450
14315
  }));
14451
14316
  let orderClause = "";
14452
- if (sortField && columns.some((c$1) => c$1.name === sortField)) orderClause = ` ORDER BY \`${sortField}\` ${sortOrder.toLowerCase() === "desc" ? "DESC" : "ASC"}`;
14317
+ if (sortField && columns.some((c$1) => c$1.name === sortField)) orderClause = ` ORDER BY \`${sortField}\` ${sortOrder === "desc" ? "DESC" : "ASC"}`;
14453
14318
  let whereClause = "";
14454
14319
  const queryParams = [];
14455
14320
  if (search) whereClause = ` WHERE ${columns.map((col) => {
@@ -14474,9 +14339,8 @@ function dataRoutes(config) {
14474
14339
  db.close();
14475
14340
  }
14476
14341
  });
14477
- app.get("/data/:name", async (c) => {
14478
- const name$1 = c.req.param("name");
14479
- if (name$1 !== path.basename(name$1) || !EXTRACTION_FILE_RE.test(name$1) || name$1.includes("..")) return c.json({ error: "Invalid extraction file name" }, 400);
14342
+ app.get("/data/:name", zValidator("param", extractionFileParamSchema, invalidParamResponse$1("Invalid extraction file name")), async (c) => {
14343
+ const { name: name$1 } = c.req.valid("param");
14480
14344
  const filePath = path.join(extractedDir, name$1);
14481
14345
  try {
14482
14346
  const content = await fs.readFile(filePath, "utf-8");
@@ -14494,11 +14358,13 @@ function dataRoutes(config) {
14494
14358
 
14495
14359
  //#endregion
14496
14360
  //#region src/server/routes/schema.ts
14497
- const SCHEMA_FILE_RE = /^[\w.-]+\.json$/;
14498
- const TABLE_NAME_RE = /^[a-z][a-z0-9_]*$/;
14499
- function resolveSchemaFile(schemaDir, name$1) {
14500
- if (name$1 !== path.basename(name$1) || !SCHEMA_FILE_RE.test(name$1) || name$1.includes("..")) return null;
14501
- return path.join(schemaDir, name$1);
14361
+ const schemaFileNameSchema = z.string().regex(/^[\w.-]+\.json$/).refine((name$1) => name$1 === path.basename(name$1) && !name$1.includes(".."));
14362
+ const schemaFileParamSchema = z.object({ name: schemaFileNameSchema });
14363
+ const tableNameParamSchema = z.object({ name: z.string().regex(/^[a-z][a-z0-9_]*$/) });
14364
+ function invalidParamResponse(message) {
14365
+ return (result, c) => {
14366
+ if (!result.success) return c.json({ error: message }, 400);
14367
+ };
14502
14368
  }
14503
14369
  function schemaRoutes(config) {
14504
14370
  const app = new Hono();
@@ -14511,23 +14377,25 @@ function schemaRoutes(config) {
14511
14377
  const jsonFiles = (await fs.readdir(schemaDir)).filter((f) => f.endsWith(".json"));
14512
14378
  return c.json(jsonFiles);
14513
14379
  });
14514
- app.get("/schema/:name", async (c) => {
14515
- const filePath = resolveSchemaFile(schemaDir, c.req.param("name"));
14516
- if (!filePath) return c.json({ error: "Invalid schema file name" }, 400);
14380
+ app.get("/schema/:name", zValidator("param", schemaFileParamSchema, invalidParamResponse("Invalid schema file name")), async (c) => {
14381
+ const { name: name$1 } = c.req.valid("param");
14382
+ const filePath = path.join(schemaDir, name$1);
14517
14383
  try {
14518
- const content = await fs.readFile(filePath, "utf-8");
14519
- return c.json(JSON.parse(content));
14384
+ return c.json(await readFile(filePath));
14520
14385
  } catch {
14521
14386
  return c.json({ error: "Schema not found" }, 404);
14522
14387
  }
14523
14388
  });
14524
- app.post("/schema/:name", async (c) => {
14525
- const filePath = resolveSchemaFile(schemaDir, c.req.param("name"));
14526
- if (!filePath) return c.json({ error: "Invalid schema file name" }, 400);
14389
+ app.post("/schema/:name", zValidator("param", schemaFileParamSchema, invalidParamResponse("Invalid schema file name")), async (c) => {
14390
+ const { name: name$1 } = c.req.valid("param");
14391
+ const filePath = path.join(schemaDir, name$1);
14527
14392
  try {
14528
14393
  const body = await c.req.json();
14529
14394
  await ensureDir();
14530
- await fs.writeFile(filePath, `${JSON.stringify(body, null, 2)}\n`);
14395
+ await writeFile(filePath, body, {
14396
+ spaces: 2,
14397
+ EOL: "\n"
14398
+ });
14531
14399
  const aiexDir = path.dirname(schemaDir);
14532
14400
  try {
14533
14401
  await savePromptSnapshot(JsonSchemaDefinitionSchema.parse(body), aiexDir);
@@ -14537,12 +14405,8 @@ function schemaRoutes(config) {
14537
14405
  return c.json({ error: "Failed to save schema" }, 500);
14538
14406
  }
14539
14407
  });
14540
- app.get("/prompt-snapshot/:name", async (c) => {
14541
- const name$1 = c.req.param("name");
14542
- if (!TABLE_NAME_RE.test(name$1)) return c.json({
14543
- success: false,
14544
- error: "Invalid table name"
14545
- }, 400);
14408
+ app.get("/prompt-snapshot/:name", zValidator("param", tableNameParamSchema, invalidParamResponse("Invalid table name")), async (c) => {
14409
+ const { name: name$1 } = c.req.valid("param");
14546
14410
  const aiexDir = path.dirname(schemaDir);
14547
14411
  const snapshotPath = path.join(aiexDir, "extracted", `${name$1}.prompt.md`);
14548
14412
  try {
@@ -14558,14 +14422,13 @@ function schemaRoutes(config) {
14558
14422
  }, 404);
14559
14423
  }
14560
14424
  });
14561
- app.delete("/schema/:name", async (c) => {
14562
- const filePath = resolveSchemaFile(schemaDir, c.req.param("name"));
14563
- if (!filePath) return c.json({ error: "Invalid schema file name" }, 400);
14425
+ app.delete("/schema/:name", zValidator("param", schemaFileParamSchema, invalidParamResponse("Invalid schema file name")), async (c) => {
14426
+ const { name: name$1 } = c.req.valid("param");
14427
+ const filePath = path.join(schemaDir, name$1);
14564
14428
  try {
14565
14429
  const aiexDir = path.dirname(schemaDir);
14566
14430
  try {
14567
- const content = await fs.readFile(filePath, "utf-8");
14568
- const parsed = JsonSchemaDefinitionSchema.safeParse(JSON.parse(content));
14431
+ const parsed = JsonSchemaDefinitionSchema.safeParse(await readFile(filePath));
14569
14432
  if (parsed.success) {
14570
14433
  const tableName = parsed.data.table.name;
14571
14434
  const snapshotPath = path.join(aiexDir, "extracted", `${tableName}.prompt.md`);
@@ -14645,25 +14508,11 @@ function createApp(config, staticDir) {
14645
14508
 
14646
14509
  //#endregion
14647
14510
  //#region src/core/web-runner.ts
14648
- const execFileAsync = promisify(execFile);
14649
14511
  function resolveWebStaticDir() {
14650
14512
  return path.join(resolvePackageRoot(), "dist/web");
14651
14513
  }
14652
14514
  async function openBrowser(url) {
14653
- if (process.platform === "darwin") {
14654
- await execFileAsync("open", [url]);
14655
- return;
14656
- }
14657
- if (process.platform === "win32") {
14658
- await execFileAsync("cmd", [
14659
- "/c",
14660
- "start",
14661
- "",
14662
- url
14663
- ]);
14664
- return;
14665
- }
14666
- await execFileAsync("xdg-open", [url]);
14515
+ await open(url);
14667
14516
  }
14668
14517
  async function startWebServer(input) {
14669
14518
  const { config, port } = input;
@@ -14744,7 +14593,7 @@ process.on("unhandledRejection", (reason) => {
14744
14593
  process.exit(1);
14745
14594
  });
14746
14595
  if (process.argv[2] === "_complete") {
14747
- const { getCompletions } = await import("./completions-ygS1okck.mjs");
14596
+ const { getCompletions } = await import("./completions-C3rmTwXZ.mjs");
14748
14597
  const suggestions = getCompletions(subCommands, process.argv.slice(3));
14749
14598
  for (const s of suggestions) process.stdout.write(`${s}\n`);
14750
14599
  process.exit(0);
@@ -1,5 +1,6 @@
1
1
  import path from "node:path";
2
2
  import process from "node:process";
3
+ import { readFileSync } from "jsonfile";
3
4
  import fs from "node:fs";
4
5
 
5
6
  //#region src/core/completions.ts
@@ -27,8 +28,7 @@ function getFileCompletions(pattern) {
27
28
  }
28
29
  function getJsonModelNames(configPath) {
29
30
  try {
30
- const content = fs.readFileSync(configPath, "utf-8");
31
- const config = JSON.parse(content);
31
+ const config = readFileSync(configPath);
32
32
  if (config.provider?.models) return config.provider.models.map((m) => m.name);
33
33
  } catch {}
34
34
  return [];