aiex-cli 0.0.1-beta.5 → 0.0.1-beta.6

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 (113) hide show
  1. package/README.md +2 -2
  2. package/dist/cli.mjs +198 -311
  3. package/dist/core/schema-sqlite/migrate-helper.mjs +14 -5
  4. package/dist/{doctor-Cd_N16iC.mjs → doctor-BTfByg-I.mjs} +2 -2
  5. package/dist/index.mjs +1 -1
  6. package/dist/web/assets/AISettings-D_AFhorO.js +334 -0
  7. package/dist/web/assets/DataBrowser-DZT0kGQE.css +1 -0
  8. package/dist/web/assets/DataBrowser-rznfVRaV.js +3 -0
  9. package/dist/web/assets/JsonSchemaEditor-C9iyQs7N.js +929 -0
  10. package/dist/web/assets/api-client-Dsg4WOM9.js +1 -0
  11. package/dist/web/assets/button-kTMweGMc.js +927 -0
  12. package/dist/web/assets/chunk-DtRyYLXJ.js +1 -0
  13. package/dist/web/assets/{cssMode-BloHqzZF.js → cssMode-DAbG0CMn.js} +1 -1
  14. package/dist/web/assets/dialog-CWuu7WjI.js +108 -0
  15. package/dist/web/assets/{editor.api-BG499EJF.js → editor.api-DLXGyrN1.js} +1 -1
  16. package/dist/web/assets/{editor.main-BhEWG0_P.js → editor.main-BqhfoHxy.js} +2 -2
  17. package/dist/web/assets/{freemarker2-DOHaFATh.js → freemarker2-B9_5ct2b.js} +1 -1
  18. package/dist/web/assets/{handlebars-BIFWety9.js → handlebars-TY59WcoQ.js} +1 -1
  19. package/dist/web/assets/{html-YGaqGZNd.js → html-CLULsh27.js} +1 -1
  20. package/dist/web/assets/{htmlMode-Bu3PyHjq.js → htmlMode-BvG7RNbU.js} +1 -1
  21. package/dist/web/assets/index-BRvFRL-3.js +882 -0
  22. package/dist/web/assets/index-DDFnprdM.css +2 -0
  23. package/dist/web/assets/{javascript-N0gZqDK0.js → javascript-DHrLp6gu.js} +1 -1
  24. package/dist/web/assets/{jsonMode-z5YscjcG.js → jsonMode-DBDhdzl1.js} +1 -1
  25. package/dist/web/assets/lib-C30cIFrm.js +1 -0
  26. package/dist/web/assets/{liquid-BHfNNVLs.js → liquid-tGeb-nqF.js} +1 -1
  27. package/dist/web/assets/{mdx-Dqu2t0et.js → mdx-Cmdz78VU.js} +1 -1
  28. package/dist/web/assets/{monaco.contribution-ByQ3yI-W.js → monaco.contribution-CroYPUF5.js} +2 -2
  29. package/dist/web/assets/overlayeventbus-AtOpmI6n.js +80 -0
  30. package/dist/web/assets/{python-icfse9Ji.js → python-Dmfz4iDE.js} +1 -1
  31. package/dist/web/assets/{razor-DwVkryG9.js → razor-BJicZHJs.js} +1 -1
  32. package/dist/web/assets/table-schema-mJrrf9qw.js +2 -0
  33. package/dist/web/assets/{tsMode-CLrI3bdf.js → tsMode-DYqTyE66.js} +1 -1
  34. package/dist/web/assets/{typescript-BzuZVF7m.js → typescript-DLnTe9Hf.js} +1 -1
  35. package/dist/web/assets/{xml-Cr85kdqA.js → xml-BIYqLORk.js} +1 -1
  36. package/dist/web/assets/{yaml-D3RbJnnO.js → yaml-BjmulkMX.js} +1 -1
  37. package/dist/web/index.html +8 -4
  38. package/package.json +1 -1
  39. package/src/core/schema-sqlite/migrate-helper.ts +9 -4
  40. package/dist/web/assets/chunk-BEqpzyXh.js +0 -1
  41. package/dist/web/assets/index-DTABZIZZ.css +0 -2
  42. package/dist/web/assets/index-DVc9DeYI.js +0 -3266
  43. /package/dist/web/assets/{abap-Cry0R76c.js → abap-DrZwwXZX.js} +0 -0
  44. /package/dist/web/assets/{apex-xqbJ58nJ.js → apex-CrCz0btt.js} +0 -0
  45. /package/dist/web/assets/{azcli-D7JTNGKs.js → azcli-BapzKHay.js} +0 -0
  46. /package/dist/web/assets/{bat-Cuq6hn0K.js → bat-C_NRAiA1.js} +0 -0
  47. /package/dist/web/assets/{bicep-eTuQjz9F.js → bicep-C7pp2CNk.js} +0 -0
  48. /package/dist/web/assets/{cameligo-DKgCRl36.js → cameligo-BhhK9vxZ.js} +0 -0
  49. /package/dist/web/assets/{clojure-B_aTBtVh.js → clojure-D0ujmUyE.js} +0 -0
  50. /package/dist/web/assets/{coffee-BWAYpIPu.js → coffee-DHEl7Jbb.js} +0 -0
  51. /package/dist/web/assets/{cpp-BduBQE8d.js → cpp-Iil-3nzZ.js} +0 -0
  52. /package/dist/web/assets/{csharp-CMqOVYKK.js → csharp-Dh0Ee7SY.js} +0 -0
  53. /package/dist/web/assets/{csp-6cGliXw2.js → csp-mwzjw0JL.js} +0 -0
  54. /package/dist/web/assets/{css-CHnKqS9Q.js → css-COIa8ZTR.js} +0 -0
  55. /package/dist/web/assets/{cypher-DMzZBj2L.js → cypher-GVc17FC4.js} +0 -0
  56. /package/dist/web/assets/{dart-7hYfJ1Dv.js → dart-phiCaE7_.js} +0 -0
  57. /package/dist/web/assets/{dockerfile-BflvjnJW.js → dockerfile-BMaDhdim.js} +0 -0
  58. /package/dist/web/assets/{ecl-BEt6xb2p.js → ecl-Cj47kvqp.js} +0 -0
  59. /package/dist/web/assets/{elixir-CnrQCt6o.js → elixir-DBbstcE1.js} +0 -0
  60. /package/dist/web/assets/{flow9-CfLCoUuB.js → flow9-ChHb1adO.js} +0 -0
  61. /package/dist/web/assets/{fsharp-BQqR9uQ6.js → fsharp-CMk2OIJN.js} +0 -0
  62. /package/dist/web/assets/{go-C3AlMVwy.js → go-BrMkuJg0.js} +0 -0
  63. /package/dist/web/assets/{graphql-O_-hDldf.js → graphql-PSR1UKGv.js} +0 -0
  64. /package/dist/web/assets/{hcl-BQQD6Mtj.js → hcl-DAQrbDOW.js} +0 -0
  65. /package/dist/web/assets/{ini-Bf0RDfP_.js → ini-0TG5BxW0.js} +0 -0
  66. /package/dist/web/assets/{java-nqX2KEDD.js → java-rgorz17v.js} +0 -0
  67. /package/dist/web/assets/{julia-B6P9U5er.js → julia-C8VMdHm8.js} +0 -0
  68. /package/dist/web/assets/{kotlin-B-LRk09-.js → kotlin-CllWo3gX.js} +0 -0
  69. /package/dist/web/assets/{less-CEaIdW1f.js → less-Cgca25AP.js} +0 -0
  70. /package/dist/web/assets/{lexon-Qv4pvFSW.js → lexon-D0GHdBaw.js} +0 -0
  71. /package/dist/web/assets/{lua-CFpyR7YN.js → lua-DmRsNG-P.js} +0 -0
  72. /package/dist/web/assets/{m3-CvKhVPQn.js → m3-BgL5dNKT.js} +0 -0
  73. /package/dist/web/assets/{markdown-qldG3Vc4.js → markdown-BuJfycGS.js} +0 -0
  74. /package/dist/web/assets/{mips-0D8PRyHq.js → mips-C9m_93PR.js} +0 -0
  75. /package/dist/web/assets/{msdax-DwZXSC5M.js → msdax-CpFHC9OI.js} +0 -0
  76. /package/dist/web/assets/{mysql-BWq85KY4.js → mysql-qFvltsqN.js} +0 -0
  77. /package/dist/web/assets/{objective-c-D653JUMG.js → objective-c-Bnmr858J.js} +0 -0
  78. /package/dist/web/assets/{pascal-rWjRDdnR.js → pascal-WP0_D5AO.js} +0 -0
  79. /package/dist/web/assets/{pascaligo-Db8EehaF.js → pascaligo-Blom4Rij.js} +0 -0
  80. /package/dist/web/assets/{perl-C68oq8-D.js → perl-B-vk8g64.js} +0 -0
  81. /package/dist/web/assets/{pgsql-BXeHe33s.js → pgsql-Cgvz6v67.js} +0 -0
  82. /package/dist/web/assets/{php-CDVsAbfl.js → php-8a3Lrw9m.js} +0 -0
  83. /package/dist/web/assets/{pla-DnryFT0q.js → pla-DuFqEZ8V.js} +0 -0
  84. /package/dist/web/assets/{postiats-CDg_4Ev-.js → postiats-DkLtSgkp.js} +0 -0
  85. /package/dist/web/assets/{powerquery-CWPi8ROz.js → powerquery-BJ1aNepW.js} +0 -0
  86. /package/dist/web/assets/{powershell-C5A0QX3-.js → powershell-rE98k687.js} +0 -0
  87. /package/dist/web/assets/{preload-helper-DSXbuxSR.js → preload-helper-DWTEM3RW.js} +0 -0
  88. /package/dist/web/assets/{protobuf-Cgt-BQbL.js → protobuf-CUheFacr.js} +0 -0
  89. /package/dist/web/assets/{pug-RPYJC9QB.js → pug-LDcAMD8w.js} +0 -0
  90. /package/dist/web/assets/{qsharp-BZ3S7fu_.js → qsharp-IHfqKOfK.js} +0 -0
  91. /package/dist/web/assets/{r-CN875f1X.js → r-D-QApv87.js} +0 -0
  92. /package/dist/web/assets/{redis-BLesvTwR.js → redis-SXdDyWR9.js} +0 -0
  93. /package/dist/web/assets/{redshift-Byf_0XqD.js → redshift-Y6lsCryn.js} +0 -0
  94. /package/dist/web/assets/{restructuredtext-DYg_6BiZ.js → restructuredtext-edObr9a8.js} +0 -0
  95. /package/dist/web/assets/{ruby-C4OkxbC-.js → ruby-CNnUfF-8.js} +0 -0
  96. /package/dist/web/assets/{rust-xAoaEFMh.js → rust-IHUZWzBr.js} +0 -0
  97. /package/dist/web/assets/{sb-C8dHOW_y.js → sb-DrUvY44N.js} +0 -0
  98. /package/dist/web/assets/{scala-Spx0wP1o.js → scala-B4hbXGLM.js} +0 -0
  99. /package/dist/web/assets/{scheme-D2mZlAUz.js → scheme-BGrd12j3.js} +0 -0
  100. /package/dist/web/assets/{scss-DDCn3Ylu.js → scss-x5G1ES4U.js} +0 -0
  101. /package/dist/web/assets/{shell-M6px0EWn.js → shell-DOehe2Y8.js} +0 -0
  102. /package/dist/web/assets/{solidity-DUWMJi-f.js → solidity-BeRvcwWV.js} +0 -0
  103. /package/dist/web/assets/{sophia-DwJbUG-2.js → sophia-DZbkUNjy.js} +0 -0
  104. /package/dist/web/assets/{sparql-ClQxbRPI.js → sparql-B7_oi5-h.js} +0 -0
  105. /package/dist/web/assets/{sql-BQdjW7Vy.js → sql-CTlsFWVE.js} +0 -0
  106. /package/dist/web/assets/{st-BpISyZ_v.js → st-DJVEJdPE.js} +0 -0
  107. /package/dist/web/assets/{swift-CMbl5gM4.js → swift-CwhT3fYa.js} +0 -0
  108. /package/dist/web/assets/{systemverilog-jx2Xs7uO.js → systemverilog-BQN63pkN.js} +0 -0
  109. /package/dist/web/assets/{tcl-GIGnfs89.js → tcl-DqwfpskA.js} +0 -0
  110. /package/dist/web/assets/{twig-Bc0mxc_m.js → twig-BiyenUgc.js} +0 -0
  111. /package/dist/web/assets/{typespec-CEioAsEm.js → typespec-CWOJribt.js} +0 -0
  112. /package/dist/web/assets/{vb-BPk67J-d.js → vb-Cq5F87m3.js} +0 -0
  113. /package/dist/web/assets/{wgsl-DOnyt8_J.js → wgsl-BAvW2lVr.js} +0 -0
package/dist/cli.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { C as doctorDiagnosticsTableRows, a as writeAIConfig, b as toSnakeCase, c as PLACEHOLDER_TEXT, d as seedConfig, f as description$1, g as createMigrationConfig, h as version, i as readAIConfig, l as AIConfigSchema, m as package_default, n as getDefaultAIConfig, o as DEFAULT_PROMPT_CONFIG, p as name, r as maskApiKey, s as PLACEHOLDER_SCHEMA, t as collectDoctorDiagnostics, u as createConfig, v as JsonSchemaDefinitionSchema, w as formatDoctorDiagnosticsJson, x as generateDrizzleSchema, y as parseJsonSchema } from "./doctor-Cd_N16iC.mjs";
1
+ import { C as doctorDiagnosticsTableRows, a as writeAIConfig, b as toSnakeCase, c as PLACEHOLDER_TEXT, d as seedConfig, f as description, g as createMigrationConfig, h as version, i as readAIConfig, l as AIConfigSchema, m as package_default, n as getDefaultAIConfig, o as DEFAULT_PROMPT_CONFIG, p as name, r as maskApiKey, s as PLACEHOLDER_SCHEMA, t as collectDoctorDiagnostics, u as createConfig, v as JsonSchemaDefinitionSchema, w as formatDoctorDiagnosticsJson, x as generateDrizzleSchema, y as parseJsonSchema } from "./doctor-BTfByg-I.mjs";
2
2
  import { createRequire } from "node:module";
3
3
  import path from "node:path";
4
4
  import process from "node:process";
@@ -12702,248 +12702,6 @@ function lookupModelCapabilities(modelName) {
12702
12702
  return null;
12703
12703
  }
12704
12704
 
12705
- //#endregion
12706
- //#region schemas/table-schema.json
12707
- var $id = "https://raw.githubusercontent.com/OSpoon/aiex-cli/main/app/cli/schemas/table-schema.json";
12708
- var table_schema_default = {
12709
- $schema: "http://json-schema.org/draft-07/schema#",
12710
- $id,
12711
- title: "aiex Table Schema",
12712
- description: "Defines a database table and its columns for aiex-cli schema-to-SQLite migration.",
12713
- type: "object",
12714
- required: [
12715
- "title",
12716
- "type",
12717
- "table",
12718
- "properties"
12719
- ],
12720
- additionalProperties: false,
12721
- properties: {
12722
- "$schema": {
12723
- "type": "string",
12724
- "description": "Pointer to this schema file for IDE validation.",
12725
- "format": "uri",
12726
- "default": "https://raw.githubusercontent.com/OSpoon/aiex-cli/main/app/cli/schemas/table-schema.json"
12727
- },
12728
- "title": {
12729
- "type": "string",
12730
- "minLength": 1,
12731
- "description": "Human-readable table title (e.g. 'User', 'OrderItem')."
12732
- },
12733
- "description": {
12734
- "type": "string",
12735
- "description": "Optional table description."
12736
- },
12737
- "type": {
12738
- "type": "string",
12739
- "const": "object",
12740
- "description": "Must be 'object' — each file defines one table."
12741
- },
12742
- "table": {
12743
- "$ref": "#/$defs/tableConfig",
12744
- "description": "Table-level configuration."
12745
- },
12746
- "properties": {
12747
- "$ref": "#/$defs/properties",
12748
- "description": "Column definitions keyed by property name (camelCase)."
12749
- },
12750
- "required": {
12751
- "type": "array",
12752
- "items": { "type": "string" },
12753
- "description": "List of property names that are NOT NULL.",
12754
- "default": []
12755
- }
12756
- },
12757
- $defs: {
12758
- "tableConfig": {
12759
- "type": "object",
12760
- "required": ["name"],
12761
- "additionalProperties": false,
12762
- "properties": {
12763
- "name": {
12764
- "type": "string",
12765
- "minLength": 1,
12766
- "pattern": "^[a-z][a-z0-9_]*$",
12767
- "description": "SQLite table name in snake_case (lowercase letters, digits, underscores).",
12768
- "examples": [
12769
- "users",
12770
- "order_items",
12771
- "blog_posts"
12772
- ]
12773
- },
12774
- "timestamps": {
12775
- "type": "boolean",
12776
- "default": false,
12777
- "description": "Automatically add created_at and updated_at TIMESTAMP columns."
12778
- },
12779
- "softDelete": {
12780
- "type": "boolean",
12781
- "default": false,
12782
- "description": "Automatically add deleted_at nullable TIMESTAMP column."
12783
- }
12784
- }
12785
- },
12786
- "properties": {
12787
- "type": "object",
12788
- "minProperties": 1,
12789
- "additionalProperties": { "$ref": "#/$defs/property" },
12790
- "description": "Column definitions. Keys are camelCase property names."
12791
- },
12792
- "property": {
12793
- "type": "object",
12794
- "required": ["type"],
12795
- "additionalProperties": false,
12796
- "properties": {
12797
- "type": {
12798
- "$ref": "#/$defs/columnType",
12799
- "description": "Column data type."
12800
- },
12801
- "format": {
12802
- "$ref": "#/$defs/formatType",
12803
- "description": "Semantic format hint that influences the SQLite column type."
12804
- },
12805
- "primary": {
12806
- "type": "boolean",
12807
- "default": false,
12808
- "description": "Mark this column as PRIMARY KEY. Only one per table."
12809
- },
12810
- "autoIncrement": {
12811
- "type": "boolean",
12812
- "default": false,
12813
- "description": "AUTO_INCREMENT for integer primary keys."
12814
- },
12815
- "unique": {
12816
- "type": "boolean",
12817
- "default": false,
12818
- "description": "Add UNIQUE constraint."
12819
- },
12820
- "default": { "description": "Default value for the column. Type should match the column type." },
12821
- "minLength": {
12822
- "type": "integer",
12823
- "minimum": 0,
12824
- "description": "Minimum string length (validation only, not a DB constraint)."
12825
- },
12826
- "maxLength": {
12827
- "type": "integer",
12828
- "minimum": 1,
12829
- "description": "Maximum string length (validation only, not a DB constraint)."
12830
- },
12831
- "minimum": {
12832
- "type": "number",
12833
- "description": "Minimum numeric value (validation only, not a DB constraint)."
12834
- },
12835
- "maximum": {
12836
- "type": "number",
12837
- "description": "Maximum numeric value (validation only, not a DB constraint)."
12838
- },
12839
- "drizzle": {
12840
- "$ref": "#/$defs/drizzleConfig",
12841
- "description": "Drizzle ORM specific overrides."
12842
- },
12843
- "nested": {
12844
- "$ref": "#/$defs/nestedConfig",
12845
- "description": "Create a separate related table instead of embedding as JSON."
12846
- },
12847
- "foreignKey": {
12848
- "$ref": "#/$defs/foreignKeyRef",
12849
- "description": "Foreign key reference to another table."
12850
- },
12851
- "properties": {
12852
- "$ref": "#/$defs/properties",
12853
- "description": "Sub-properties when type is 'object'. Ignored unless nested.enabled or drizzle.mode='json'."
12854
- },
12855
- "items": {
12856
- "$ref": "#/$defs/property",
12857
- "description": "Item schema when type is 'array'. Ignored unless items.nested.enabled."
12858
- },
12859
- "required": {
12860
- "type": "array",
12861
- "items": { "type": "string" },
12862
- "description": "Required sub-properties for nested objects."
12863
- }
12864
- }
12865
- },
12866
- "columnType": {
12867
- "type": "string",
12868
- "enum": [
12869
- "string",
12870
- "integer",
12871
- "number",
12872
- "boolean",
12873
- "object",
12874
- "array",
12875
- "null"
12876
- ],
12877
- "description": "Data type. Maps to SQLite as: string→TEXT, integer→INTEGER, number→REAL, boolean→INTEGER(boolean), object→TEXT(json), array→TEXT(json), null→TEXT."
12878
- },
12879
- "formatType": {
12880
- "type": "string",
12881
- "enum": [
12882
- "date-time",
12883
- "email",
12884
- "uri",
12885
- "json"
12886
- ],
12887
- "description": "Format hints: 'date-time' → INTEGER(timestamp), 'json' → TEXT(json), 'email'/'uri' → TEXT (no special mapping)."
12888
- },
12889
- "drizzleConfig": {
12890
- "type": "object",
12891
- "additionalProperties": false,
12892
- "properties": {
12893
- "mode": {
12894
- "type": "string",
12895
- "enum": [
12896
- "json",
12897
- "timestamp",
12898
- "timestamp_ms",
12899
- "boolean",
12900
- "bigint"
12901
- ],
12902
- "description": "Override Drizzle column mode. 'json' stores as TEXT(json), 'timestamp'/'timestamp_ms' as INTEGER, 'boolean' as INTEGER(boolean), 'bigint' as INTEGER(bigint)."
12903
- },
12904
- "customType": {
12905
- "type": "string",
12906
- "description": "Custom Drizzle type name (reserved for future use)."
12907
- }
12908
- }
12909
- },
12910
- "nestedConfig": {
12911
- "type": "object",
12912
- "required": ["enabled", "relation"],
12913
- "additionalProperties": false,
12914
- "properties": {
12915
- "enabled": {
12916
- "type": "boolean",
12917
- "const": true,
12918
- "description": "Must be true to activate nested table creation."
12919
- },
12920
- "relation": {
12921
- "type": "string",
12922
- "enum": ["has-one", "has-many"],
12923
- "description": "'has-one' → one() relation, 'has-many' → many() relation. Only one level of nesting is supported."
12924
- }
12925
- }
12926
- },
12927
- "foreignKeyRef": {
12928
- "type": "object",
12929
- "required": ["table", "column"],
12930
- "additionalProperties": false,
12931
- "properties": {
12932
- "table": {
12933
- "type": "string",
12934
- "minLength": 1,
12935
- "description": "Target table name (snake_case)."
12936
- },
12937
- "column": {
12938
- "type": "string",
12939
- "minLength": 1,
12940
- "description": "Target column name in the referenced table."
12941
- }
12942
- }
12943
- }
12944
- }
12945
- };
12946
-
12947
12705
  //#endregion
12948
12706
  //#region src/core/ai-extraction/json-utils.ts
12949
12707
  function stripFences(text) {
@@ -13132,6 +12890,100 @@ async function readFilePart(filePath) {
13132
12890
  mimeType: mime
13133
12891
  };
13134
12892
  }
12893
+ function nullableType(type) {
12894
+ return type === "null" ? ["null"] : [type, "null"];
12895
+ }
12896
+ function propertyToExtractionSchema(property) {
12897
+ if (property.type === "array") return {
12898
+ type: nullableType("array"),
12899
+ items: property.items ? propertyToExtractionSchema(property.items) : {}
12900
+ };
12901
+ if (property.type === "object") {
12902
+ const childProperties = property.properties ? Object.fromEntries(Object.entries(property.properties).map(([name$1, prop]) => [name$1, propertyToExtractionSchema(prop)])) : void 0;
12903
+ return {
12904
+ type: nullableType("object"),
12905
+ ...childProperties ? {
12906
+ properties: childProperties,
12907
+ required: Object.keys(childProperties),
12908
+ additionalProperties: false
12909
+ } : { additionalProperties: true }
12910
+ };
12911
+ }
12912
+ return { type: nullableType(property.type) };
12913
+ }
12914
+ function isRecord(value) {
12915
+ return typeof value === "object" && value !== null && !Array.isArray(value);
12916
+ }
12917
+ function schemaToExtractionOutputSchema(schema) {
12918
+ const properties = Object.fromEntries(Object.entries(schema.properties).filter(([, prop]) => !(prop.primary && prop.autoIncrement)).map(([name$1, prop]) => [name$1, propertyToExtractionSchema(prop)]));
12919
+ return {
12920
+ type: "object",
12921
+ additionalProperties: false,
12922
+ properties,
12923
+ required: Object.keys(properties)
12924
+ };
12925
+ }
12926
+ function validatePropertyValue(path$1, property, value, issues) {
12927
+ if (value === null) return;
12928
+ switch (property.type) {
12929
+ case "string":
12930
+ if (typeof value !== "string") issues.push(`${path$1}: expected string or null`);
12931
+ return;
12932
+ case "integer":
12933
+ if (!Number.isInteger(value)) issues.push(`${path$1}: expected integer or null`);
12934
+ return;
12935
+ case "number":
12936
+ if (typeof value !== "number" || Number.isNaN(value)) issues.push(`${path$1}: expected number or null`);
12937
+ return;
12938
+ case "boolean":
12939
+ if (typeof value !== "boolean") issues.push(`${path$1}: expected boolean or null`);
12940
+ return;
12941
+ case "array":
12942
+ if (!Array.isArray(value)) {
12943
+ issues.push(`${path$1}: expected array or null`);
12944
+ return;
12945
+ }
12946
+ if (property.items) {
12947
+ const itemProperty = property.items;
12948
+ value.forEach((item, index) => validatePropertyValue(`${path$1}[${index}]`, itemProperty, item, issues));
12949
+ }
12950
+ return;
12951
+ case "object":
12952
+ if (!isRecord(value)) {
12953
+ issues.push(`${path$1}: expected object or null`);
12954
+ return;
12955
+ }
12956
+ if (property.properties) validateProperties(path$1, property.properties, value, issues);
12957
+ return;
12958
+ case "null": issues.push(`${path$1}: expected null`);
12959
+ }
12960
+ }
12961
+ function validateProperties(basePath, properties, data, issues) {
12962
+ const expected = Object.entries(properties).filter(([, prop]) => !(prop.primary && prop.autoIncrement));
12963
+ const expectedKeys = new Set(expected.map(([name$1]) => name$1));
12964
+ for (const key of Object.keys(data)) if (!expectedKeys.has(key)) issues.push(`${basePath}.${key}: unexpected field`);
12965
+ for (const [name$1, prop] of expected) {
12966
+ const path$1 = `${basePath}.${name$1}`;
12967
+ if (!(name$1 in data)) {
12968
+ issues.push(`${path$1}: missing field`);
12969
+ continue;
12970
+ }
12971
+ validatePropertyValue(path$1, prop, data[name$1], issues);
12972
+ }
12973
+ }
12974
+ function validateExtractedData(schema, data) {
12975
+ if (!isRecord(data)) return {
12976
+ success: false,
12977
+ error: "Extracted data must be a JSON object."
12978
+ };
12979
+ const issues = [];
12980
+ validateProperties("$", schema.properties, data, issues);
12981
+ if (issues.length > 0) return {
12982
+ success: false,
12983
+ error: `Extracted data does not match schema:\n${issues.map((issue) => ` - ${issue}`).join("\n")}`
12984
+ };
12985
+ return { success: true };
12986
+ }
13135
12987
  async function loadPromptSnapshot(aiexDir, tableName) {
13136
12988
  const snapshotPath = path.join(aiexDir, "extracted", `${tableName}.prompt.md`);
13137
12989
  try {
@@ -13173,15 +13025,16 @@ async function extractStructuredData(input) {
13173
13025
  let system;
13174
13026
  let user;
13175
13027
  const snapshot = await loadPromptSnapshot(aiexDir, schema.table.name);
13028
+ const promptText = file ? PLACEHOLDER_TEXT : text;
13176
13029
  if (snapshot) {
13177
13030
  system = snapshot.system;
13178
- user = snapshot.user.replaceAll(PLACEHOLDER_TEXT, text);
13031
+ user = snapshot.user.replaceAll(PLACEHOLDER_TEXT, promptText);
13179
13032
  } else {
13180
- const generated = generateExtractionPrompt(schema, text, config.prompt ?? DEFAULT_PROMPT_CONFIG);
13033
+ const generated = generateExtractionPrompt(schema, promptText, config.prompt ?? DEFAULT_PROMPT_CONFIG);
13181
13034
  system = generated.system;
13182
13035
  user = generated.user;
13183
13036
  }
13184
- const outputSchema = jsonSchema(table_schema_default);
13037
+ const outputSchema = jsonSchema(schemaToExtractionOutputSchema(schema));
13185
13038
  let result;
13186
13039
  if (useFileContent) {
13187
13040
  const filePart = await readFilePart(file);
@@ -13225,6 +13078,11 @@ async function extractStructuredData(input) {
13225
13078
  let data;
13226
13079
  if (useStructuredOutput) data = result.output;
13227
13080
  else data = safeParseJSON(result.text);
13081
+ const validation = validateExtractedData(schema, data);
13082
+ if (!validation.success) return {
13083
+ success: false,
13084
+ error: validation.error
13085
+ };
13228
13086
  const outputDir = path.resolve(aiexDir, config.extraction.outputDir.replace(".aiex/", ""));
13229
13087
  await fs.mkdir(outputDir, { recursive: true });
13230
13088
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
@@ -13409,6 +13267,12 @@ const IMAGE_EXTENSIONS = new Set([
13409
13267
  "bmp",
13410
13268
  "svg"
13411
13269
  ]);
13270
+ const FILE_PART_EXTENSIONS = new Set([...IMAGE_EXTENSIONS, "pdf"]);
13271
+ function fail$1(message) {
13272
+ if (message) consola.error(message);
13273
+ outro("Failed!");
13274
+ process.exitCode = 1;
13275
+ }
13412
13276
  async function ensureDatabaseReady(dbPath, schema) {
13413
13277
  try {
13414
13278
  await fs.access(dbPath);
@@ -13467,29 +13331,24 @@ const extractCommand = defineCommand({
13467
13331
  const config = createMigrationConfig(process.cwd());
13468
13332
  const aiexDir = path.dirname(config.schemaPath);
13469
13333
  if (!args.text && !args.file) {
13470
- consola.error("Please provide text (-t) or a file (-f) to extract from");
13471
- outro("Failed!");
13334
+ fail$1("Please provide text (-t) or a file (-f) to extract from");
13472
13335
  return;
13473
13336
  }
13474
13337
  if (args.text && args.file) {
13475
- consola.error("-t and -f cannot be used together");
13476
- outro("Failed!");
13338
+ fail$1("-t and -f cannot be used together");
13477
13339
  return;
13478
13340
  }
13479
13341
  const aiConfig = await readAIConfig(aiexDir);
13480
13342
  if (!aiConfig) {
13481
- consola.error("AI configuration not found. Please configure AI settings in the Web interface first");
13482
- outro("Failed!");
13343
+ fail$1("AI configuration not found. Please configure AI settings in the Web interface first");
13483
13344
  return;
13484
13345
  }
13485
13346
  if (!aiConfig.provider.apiKey) {
13486
- consola.error("API Key not configured. Please configure AI settings in the Web interface first");
13487
- outro("Failed!");
13347
+ fail$1("API Key not configured. Please configure AI settings in the Web interface first");
13488
13348
  return;
13489
13349
  }
13490
13350
  if (!aiConfig.provider.models?.length) {
13491
- consola.error("No models configured. Please add at least one model in AI Settings");
13492
- outro("Failed!");
13351
+ fail$1("No models configured. Please add at least one model in AI Settings");
13493
13352
  return;
13494
13353
  }
13495
13354
  let modelOverride;
@@ -13497,8 +13356,7 @@ const extractCommand = defineCommand({
13497
13356
  const matched = aiConfig.provider.models.find((m) => m.name === args.model);
13498
13357
  if (!matched) {
13499
13358
  const available = aiConfig.provider.models.map((m) => m.name).join(", ");
13500
- consola.error(`Model "${args.model}" not found in configuration. Available models: ${available}`);
13501
- outro("Failed!");
13359
+ fail$1(`Model "${args.model}" not found in configuration. Available models: ${available}`);
13502
13360
  return;
13503
13361
  }
13504
13362
  modelOverride = matched;
@@ -13507,12 +13365,11 @@ const extractCommand = defineCommand({
13507
13365
  let filePath;
13508
13366
  if (args.file) {
13509
13367
  const ext = path.extname(args.file).toLowerCase().replace(".", "");
13510
- if (IMAGE_EXTENSIONS.has(ext)) filePath = args.file;
13368
+ if (FILE_PART_EXTENSIONS.has(ext)) filePath = args.file;
13511
13369
  else try {
13512
13370
  text = await fs.readFile(args.file, "utf-8");
13513
13371
  } catch {
13514
- consola.error(`Cannot read file: ${args.file}`);
13515
- outro("Failed!");
13372
+ fail$1(`Cannot read file: ${args.file}`);
13516
13373
  return;
13517
13374
  }
13518
13375
  } else if (args.text) text = args.text;
@@ -13523,18 +13380,17 @@ const extractCommand = defineCommand({
13523
13380
  const content = await fs.readFile(schemaPath, "utf-8");
13524
13381
  schema = JSON.parse(content);
13525
13382
  } catch {
13526
- consola.error(`Cannot read schema file: ${schemaName}.json`);
13527
- outro("Failed!");
13383
+ fail$1(`Cannot read schema file: ${schemaName}.json`);
13528
13384
  return;
13529
13385
  }
13530
13386
  try {
13531
- JsonSchemaDefinitionSchema.parse(schema);
13387
+ schema = JsonSchemaDefinitionSchema.parse(schema);
13532
13388
  } catch (e) {
13533
13389
  if (e instanceof ZodError) {
13534
13390
  consola.error(`Schema validation failed: ${schemaName}.json`);
13535
13391
  for (const issue of e.issues) consola.error(` - ${issue.path.join(".")}: ${issue.message}`);
13536
13392
  }
13537
- outro("Failed!");
13393
+ fail$1();
13538
13394
  return;
13539
13395
  }
13540
13396
  const s = spinner();
@@ -13549,8 +13405,7 @@ const extractCommand = defineCommand({
13549
13405
  });
13550
13406
  if (!result.success) {
13551
13407
  s.stop("Extraction failed");
13552
- consola.error(result.error || "Unknown error");
13553
- outro("Failed!");
13408
+ fail$1(result.error || "Unknown error");
13554
13409
  return;
13555
13410
  }
13556
13411
  s.stop("Extraction complete");
@@ -13562,8 +13417,7 @@ const extractCommand = defineCommand({
13562
13417
  const dbError = await ensureDatabaseReady(config.databasePath, schema);
13563
13418
  if (dbError) {
13564
13419
  s2.stop("Database not ready");
13565
- consola.error(dbError);
13566
- outro("Failed!");
13420
+ fail$1(dbError);
13567
13421
  return;
13568
13422
  }
13569
13423
  try {
@@ -13573,8 +13427,7 @@ const extractCommand = defineCommand({
13573
13427
  if (insertResult.success) s2.stop(`Inserted into ${insertResult.tablesInserted.length} table(s)`);
13574
13428
  else {
13575
13429
  s2.stop("Database insert failed");
13576
- consola.error(insertResult.error || "Unknown error");
13577
- outro("Failed!");
13430
+ fail$1(insertResult.error || "Unknown error");
13578
13431
  return;
13579
13432
  }
13580
13433
  } finally {
@@ -13582,8 +13435,7 @@ const extractCommand = defineCommand({
13582
13435
  }
13583
13436
  } catch (e) {
13584
13437
  s2.stop("Database insert failed");
13585
- consola.error(e instanceof Error ? e.message : String(e));
13586
- outro("Failed!");
13438
+ fail$1(e instanceof Error ? e.message : String(e));
13587
13439
  return;
13588
13440
  }
13589
13441
  }
@@ -13591,9 +13443,27 @@ const extractCommand = defineCommand({
13591
13443
  }
13592
13444
  });
13593
13445
 
13446
+ //#endregion
13447
+ //#region schemas/table-schema.json
13448
+ var $id = "https://raw.githubusercontent.com/OSpoon/aiex-cli/main/app/cli/schemas/table-schema.json";
13449
+
13594
13450
  //#endregion
13595
13451
  //#region src/commands/schema.ts
13596
13452
  const execFileAsync$1 = promisify(execFile);
13453
+ function fail(message) {
13454
+ if (message) consola.error(message);
13455
+ outro("Failed!");
13456
+ process.exitCode = 1;
13457
+ }
13458
+ async function writeJsonIfAbsent(filePath, data) {
13459
+ try {
13460
+ await fs.writeFile(filePath, `${JSON.stringify(data, null, 2)}\n`, { flag: "wx" });
13461
+ return "created";
13462
+ } catch (error) {
13463
+ if (error.code === "EEXIST") return "skipped";
13464
+ throw error;
13465
+ }
13466
+ }
13597
13467
  async function generateFromFiles(schemaFiles, config) {
13598
13468
  const result = parseAllSchemas(await Promise.all(schemaFiles.map(async (filePath) => {
13599
13469
  return {
@@ -13611,17 +13481,18 @@ async function generateFromFiles(schemaFiles, config) {
13611
13481
  consola.success(`Generated ${pc.cyan(".aiex/drizzle/schema.ts")} from ${schemaFiles.length} schema file(s)`);
13612
13482
  return true;
13613
13483
  }
13614
- async function migrate(config) {
13484
+ async function migrate(config, migrationName) {
13615
13485
  const helperPath = resolveHelperPath();
13616
- const tsxPath = resolveTsxPath();
13486
+ const helperArgs = [
13487
+ resolveTsxPath(),
13488
+ helperPath,
13489
+ config.drizzleSchemaPath,
13490
+ config.migrationsPath,
13491
+ config.databasePath
13492
+ ];
13493
+ if (migrationName) helperArgs.push(migrationName);
13617
13494
  try {
13618
- const { stdout } = await execFileAsync$1(process.execPath, [
13619
- tsxPath,
13620
- helperPath,
13621
- config.drizzleSchemaPath,
13622
- config.migrationsPath,
13623
- config.databasePath
13624
- ], { cwd: process.cwd() });
13495
+ const { stdout } = await execFileAsync$1(process.execPath, helperArgs, { cwd: process.cwd() });
13625
13496
  const result = JSON.parse(stdout.trim());
13626
13497
  if (!result.success) {
13627
13498
  consola.error("Failed to generate migration");
@@ -13862,9 +13733,11 @@ const schemaCommand = defineCommand({
13862
13733
  "authorId"
13863
13734
  ]
13864
13735
  };
13865
- await fs.writeFile(path.join(config.schemaPath, "user.json"), `${JSON.stringify(userSchema, null, 2)}\n`);
13866
- await fs.writeFile(path.join(config.schemaPath, "post.json"), `${JSON.stringify(postSchema, null, 2)}\n`);
13736
+ const userStatus = await writeJsonIfAbsent(path.join(config.schemaPath, "user.json"), userSchema);
13737
+ const postStatus = await writeJsonIfAbsent(path.join(config.schemaPath, "post.json"), postSchema);
13867
13738
  consola.success(`Initialized ${pc.cyan(".aiex/")} with example schemas`);
13739
+ if (userStatus === "skipped") consola.warn(`${pc.cyan(".aiex/schema/user.json")} already exists, skipped`);
13740
+ if (postStatus === "skipped") consola.warn(`${pc.cyan(".aiex/schema/post.json")} already exists, skipped`);
13868
13741
  consola.info("Example includes: User (with preferences has-one), Post (with comments has-many)");
13869
13742
  outro("Run: aiex schema");
13870
13743
  return;
@@ -13877,9 +13750,8 @@ const schemaCommand = defineCommand({
13877
13750
  schemaFiles = [];
13878
13751
  }
13879
13752
  if (schemaFiles.length === 0) {
13880
- consola.error(`No schema files found in ${pc.cyan(".aiex/schema/")}`);
13881
13753
  consola.info("Use --init to initialize with an example schema");
13882
- outro("Failed!");
13754
+ fail(`No schema files found in ${pc.cyan(".aiex/schema/")}`);
13883
13755
  return;
13884
13756
  }
13885
13757
  const s1 = spinner();
@@ -13887,7 +13759,7 @@ const schemaCommand = defineCommand({
13887
13759
  const genOk = await generateFromFiles(schemaFiles, config);
13888
13760
  s1.stop(genOk ? "Schema generated" : "Generation failed");
13889
13761
  if (!genOk) {
13890
- outro("Failed!");
13762
+ fail();
13891
13763
  return;
13892
13764
  }
13893
13765
  if (args.generate) {
@@ -13896,10 +13768,10 @@ const schemaCommand = defineCommand({
13896
13768
  }
13897
13769
  const s2 = spinner();
13898
13770
  s2.start("Running migrations...");
13899
- const migOk = await migrate(config);
13771
+ const migOk = await migrate(config, args.name);
13900
13772
  s2.stop(migOk ? "Migrations applied" : "Migration failed");
13901
13773
  if (!migOk) {
13902
- outro("Failed!");
13774
+ fail();
13903
13775
  return;
13904
13776
  }
13905
13777
  outro("Done!");
@@ -13977,6 +13849,8 @@ function aiRoutes(config) {
13977
13849
  //#endregion
13978
13850
  //#region src/server/routes/data.ts
13979
13851
  const FILE_REGEX = /\.json$/;
13852
+ const EXTRACTION_FILE_RE = /^[\w.-]+\.json$/;
13853
+ const TABLE_NAME_RE$1 = /^[a-z][a-z0-9_]*$/;
13980
13854
  const TIMESTAMP_CLEANUP = /(\d{2})-(\d{2})-(\d{2})/;
13981
13855
  const TIMESTAMP_TZ = /(\d{3})Z/;
13982
13856
  function dataRoutes(config) {
@@ -14014,20 +13888,6 @@ function dataRoutes(config) {
14014
13888
  return c.json({ error: error instanceof Error ? error.message : String(error) }, 500);
14015
13889
  }
14016
13890
  });
14017
- app.get("/data/:name", async (c) => {
14018
- const name$1 = c.req.param("name");
14019
- const filePath = path.join(extractedDir, name$1);
14020
- try {
14021
- const content = await fs.readFile(filePath, "utf-8");
14022
- return c.json({
14023
- success: true,
14024
- content,
14025
- name: name$1
14026
- });
14027
- } catch {
14028
- return c.json({ error: "Extraction result not found" }, 404);
14029
- }
14030
- });
14031
13891
  app.get("/data/tables", async (c) => {
14032
13892
  try {
14033
13893
  const schemaDir = config.schemaPath;
@@ -14066,6 +13926,7 @@ function dataRoutes(config) {
14066
13926
  });
14067
13927
  app.get("/data/tables/:name", async (c) => {
14068
13928
  const tableName = c.req.param("name");
13929
+ if (!TABLE_NAME_RE$1.test(tableName)) return c.json({ error: "Invalid table name" }, 400);
14069
13930
  const sortField = c.req.query("sortField");
14070
13931
  const sortOrder = c.req.query("sortOrder") || "asc";
14071
13932
  let db;
@@ -14100,12 +13961,33 @@ function dataRoutes(config) {
14100
13961
  db.close();
14101
13962
  }
14102
13963
  });
13964
+ app.get("/data/:name", async (c) => {
13965
+ const name$1 = c.req.param("name");
13966
+ 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);
13967
+ const filePath = path.join(extractedDir, name$1);
13968
+ try {
13969
+ const content = await fs.readFile(filePath, "utf-8");
13970
+ return c.json({
13971
+ success: true,
13972
+ content,
13973
+ name: name$1
13974
+ });
13975
+ } catch {
13976
+ return c.json({ error: "Extraction result not found" }, 404);
13977
+ }
13978
+ });
14103
13979
  return app;
14104
13980
  }
14105
13981
 
14106
13982
  //#endregion
14107
13983
  //#region src/server/routes/schema.ts
14108
13984
  const execFileAsync = promisify(execFile);
13985
+ const SCHEMA_FILE_RE = /^[\w.-]+\.json$/;
13986
+ const TABLE_NAME_RE = /^[a-z][a-z0-9_]*$/;
13987
+ function resolveSchemaFile(schemaDir, name$1) {
13988
+ if (name$1 !== path.basename(name$1) || !SCHEMA_FILE_RE.test(name$1) || name$1.includes("..")) return null;
13989
+ return path.join(schemaDir, name$1);
13990
+ }
14109
13991
  function schemaRoutes(config) {
14110
13992
  const app = new Hono();
14111
13993
  const schemaDir = config.schemaPath;
@@ -14118,8 +14000,8 @@ function schemaRoutes(config) {
14118
14000
  return c.json(jsonFiles);
14119
14001
  });
14120
14002
  app.get("/schema/:name", async (c) => {
14121
- const name$1 = c.req.param("name");
14122
- const filePath = path.join(schemaDir, name$1);
14003
+ const filePath = resolveSchemaFile(schemaDir, c.req.param("name"));
14004
+ if (!filePath) return c.json({ error: "Invalid schema file name" }, 400);
14123
14005
  try {
14124
14006
  const content = await fs.readFile(filePath, "utf-8");
14125
14007
  return c.json(JSON.parse(content));
@@ -14128,8 +14010,8 @@ function schemaRoutes(config) {
14128
14010
  }
14129
14011
  });
14130
14012
  app.post("/schema/:name", async (c) => {
14131
- const name$1 = c.req.param("name");
14132
- const filePath = path.join(schemaDir, name$1);
14013
+ const filePath = resolveSchemaFile(schemaDir, c.req.param("name"));
14014
+ if (!filePath) return c.json({ error: "Invalid schema file name" }, 400);
14133
14015
  try {
14134
14016
  const body = await c.req.json();
14135
14017
  await ensureDir();
@@ -14145,6 +14027,10 @@ function schemaRoutes(config) {
14145
14027
  });
14146
14028
  app.get("/prompt-snapshot/:name", async (c) => {
14147
14029
  const name$1 = c.req.param("name");
14030
+ if (!TABLE_NAME_RE.test(name$1)) return c.json({
14031
+ success: false,
14032
+ error: "Invalid table name"
14033
+ }, 400);
14148
14034
  const aiexDir = path.dirname(schemaDir);
14149
14035
  const snapshotPath = path.join(aiexDir, "extracted", `${name$1}.prompt.md`);
14150
14036
  try {
@@ -14161,8 +14047,8 @@ function schemaRoutes(config) {
14161
14047
  }
14162
14048
  });
14163
14049
  app.delete("/schema/:name", async (c) => {
14164
- const name$1 = c.req.param("name");
14165
- const filePath = path.join(schemaDir, name$1);
14050
+ const filePath = resolveSchemaFile(schemaDir, c.req.param("name"));
14051
+ if (!filePath) return c.json({ error: "Invalid schema file name" }, 400);
14166
14052
  try {
14167
14053
  await fs.unlink(filePath);
14168
14054
  return c.json({ success: true });
@@ -14239,9 +14125,10 @@ function schemaRoutes(config) {
14239
14125
 
14240
14126
  //#endregion
14241
14127
  //#region src/server/index.ts
14128
+ const LOCAL_ORIGIN_RE = /^https?:\/\/(?:localhost|127\.0\.0\.1|\[::1\])(?::\d+)?$/;
14242
14129
  function createApp(config, staticDir) {
14243
14130
  const app = new Hono();
14244
- app.use("*", cors());
14131
+ app.use("*", cors({ origin: (origin) => LOCAL_ORIGIN_RE.test(origin) ? origin : null }));
14245
14132
  app.route("/api", schemaRoutes(config));
14246
14133
  app.route("/api", aiRoutes(config));
14247
14134
  app.route("/api", dataRoutes(config));
@@ -14328,7 +14215,7 @@ runMain(defineCommand({
14328
14215
  meta: {
14329
14216
  name,
14330
14217
  version,
14331
- description: description$1
14218
+ description
14332
14219
  },
14333
14220
  subCommands
14334
14221
  }));