aiex-cli 0.0.1-beta.5 → 0.0.1-beta.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -2
- package/dist/cli.mjs +303 -315
- package/dist/completions-ygS1okck.mjs +90 -0
- package/dist/core/schema-sqlite/migrate-helper.mjs +14 -5
- package/dist/{doctor-Cd_N16iC.mjs → doctor-SWEG-HYl.mjs} +6 -3
- package/dist/index.mjs +1 -1
- package/dist/web/assets/AISettings-D_AFhorO.js +334 -0
- package/dist/web/assets/DataBrowser-DZT0kGQE.css +1 -0
- package/dist/web/assets/DataBrowser-rznfVRaV.js +3 -0
- package/dist/web/assets/JsonSchemaEditor-C9iyQs7N.js +929 -0
- package/dist/web/assets/api-client-Dsg4WOM9.js +1 -0
- package/dist/web/assets/button-kTMweGMc.js +927 -0
- package/dist/web/assets/chunk-DtRyYLXJ.js +1 -0
- package/dist/web/assets/{cssMode-BloHqzZF.js → cssMode-DAbG0CMn.js} +1 -1
- package/dist/web/assets/dialog-CWuu7WjI.js +108 -0
- package/dist/web/assets/{editor.api-BG499EJF.js → editor.api-DLXGyrN1.js} +1 -1
- package/dist/web/assets/{editor.main-BhEWG0_P.js → editor.main-BqhfoHxy.js} +2 -2
- package/dist/web/assets/{freemarker2-DOHaFATh.js → freemarker2-B9_5ct2b.js} +1 -1
- package/dist/web/assets/{handlebars-BIFWety9.js → handlebars-TY59WcoQ.js} +1 -1
- package/dist/web/assets/{html-YGaqGZNd.js → html-CLULsh27.js} +1 -1
- package/dist/web/assets/{htmlMode-Bu3PyHjq.js → htmlMode-BvG7RNbU.js} +1 -1
- package/dist/web/assets/index-BRvFRL-3.js +882 -0
- package/dist/web/assets/index-DDFnprdM.css +2 -0
- package/dist/web/assets/{javascript-N0gZqDK0.js → javascript-DHrLp6gu.js} +1 -1
- package/dist/web/assets/{jsonMode-z5YscjcG.js → jsonMode-DBDhdzl1.js} +1 -1
- package/dist/web/assets/lib-C30cIFrm.js +1 -0
- package/dist/web/assets/{liquid-BHfNNVLs.js → liquid-tGeb-nqF.js} +1 -1
- package/dist/web/assets/{mdx-Dqu2t0et.js → mdx-Cmdz78VU.js} +1 -1
- package/dist/web/assets/{monaco.contribution-ByQ3yI-W.js → monaco.contribution-CroYPUF5.js} +2 -2
- package/dist/web/assets/overlayeventbus-AtOpmI6n.js +80 -0
- package/dist/web/assets/{python-icfse9Ji.js → python-Dmfz4iDE.js} +1 -1
- package/dist/web/assets/{razor-DwVkryG9.js → razor-BJicZHJs.js} +1 -1
- package/dist/web/assets/table-schema-mJrrf9qw.js +2 -0
- package/dist/web/assets/{tsMode-CLrI3bdf.js → tsMode-DYqTyE66.js} +1 -1
- package/dist/web/assets/{typescript-BzuZVF7m.js → typescript-DLnTe9Hf.js} +1 -1
- package/dist/web/assets/{xml-Cr85kdqA.js → xml-BIYqLORk.js} +1 -1
- package/dist/web/assets/{yaml-D3RbJnnO.js → yaml-BjmulkMX.js} +1 -1
- package/dist/web/index.html +8 -4
- package/package.json +5 -2
- package/src/core/schema-sqlite/migrate-helper.ts +9 -4
- package/src/core/schema-sqlite/migration-name.ts +14 -0
- package/dist/web/assets/chunk-BEqpzyXh.js +0 -1
- package/dist/web/assets/index-DTABZIZZ.css +0 -2
- package/dist/web/assets/index-DVc9DeYI.js +0 -3266
- /package/dist/web/assets/{abap-Cry0R76c.js → abap-DrZwwXZX.js} +0 -0
- /package/dist/web/assets/{apex-xqbJ58nJ.js → apex-CrCz0btt.js} +0 -0
- /package/dist/web/assets/{azcli-D7JTNGKs.js → azcli-BapzKHay.js} +0 -0
- /package/dist/web/assets/{bat-Cuq6hn0K.js → bat-C_NRAiA1.js} +0 -0
- /package/dist/web/assets/{bicep-eTuQjz9F.js → bicep-C7pp2CNk.js} +0 -0
- /package/dist/web/assets/{cameligo-DKgCRl36.js → cameligo-BhhK9vxZ.js} +0 -0
- /package/dist/web/assets/{clojure-B_aTBtVh.js → clojure-D0ujmUyE.js} +0 -0
- /package/dist/web/assets/{coffee-BWAYpIPu.js → coffee-DHEl7Jbb.js} +0 -0
- /package/dist/web/assets/{cpp-BduBQE8d.js → cpp-Iil-3nzZ.js} +0 -0
- /package/dist/web/assets/{csharp-CMqOVYKK.js → csharp-Dh0Ee7SY.js} +0 -0
- /package/dist/web/assets/{csp-6cGliXw2.js → csp-mwzjw0JL.js} +0 -0
- /package/dist/web/assets/{css-CHnKqS9Q.js → css-COIa8ZTR.js} +0 -0
- /package/dist/web/assets/{cypher-DMzZBj2L.js → cypher-GVc17FC4.js} +0 -0
- /package/dist/web/assets/{dart-7hYfJ1Dv.js → dart-phiCaE7_.js} +0 -0
- /package/dist/web/assets/{dockerfile-BflvjnJW.js → dockerfile-BMaDhdim.js} +0 -0
- /package/dist/web/assets/{ecl-BEt6xb2p.js → ecl-Cj47kvqp.js} +0 -0
- /package/dist/web/assets/{elixir-CnrQCt6o.js → elixir-DBbstcE1.js} +0 -0
- /package/dist/web/assets/{flow9-CfLCoUuB.js → flow9-ChHb1adO.js} +0 -0
- /package/dist/web/assets/{fsharp-BQqR9uQ6.js → fsharp-CMk2OIJN.js} +0 -0
- /package/dist/web/assets/{go-C3AlMVwy.js → go-BrMkuJg0.js} +0 -0
- /package/dist/web/assets/{graphql-O_-hDldf.js → graphql-PSR1UKGv.js} +0 -0
- /package/dist/web/assets/{hcl-BQQD6Mtj.js → hcl-DAQrbDOW.js} +0 -0
- /package/dist/web/assets/{ini-Bf0RDfP_.js → ini-0TG5BxW0.js} +0 -0
- /package/dist/web/assets/{java-nqX2KEDD.js → java-rgorz17v.js} +0 -0
- /package/dist/web/assets/{julia-B6P9U5er.js → julia-C8VMdHm8.js} +0 -0
- /package/dist/web/assets/{kotlin-B-LRk09-.js → kotlin-CllWo3gX.js} +0 -0
- /package/dist/web/assets/{less-CEaIdW1f.js → less-Cgca25AP.js} +0 -0
- /package/dist/web/assets/{lexon-Qv4pvFSW.js → lexon-D0GHdBaw.js} +0 -0
- /package/dist/web/assets/{lua-CFpyR7YN.js → lua-DmRsNG-P.js} +0 -0
- /package/dist/web/assets/{m3-CvKhVPQn.js → m3-BgL5dNKT.js} +0 -0
- /package/dist/web/assets/{markdown-qldG3Vc4.js → markdown-BuJfycGS.js} +0 -0
- /package/dist/web/assets/{mips-0D8PRyHq.js → mips-C9m_93PR.js} +0 -0
- /package/dist/web/assets/{msdax-DwZXSC5M.js → msdax-CpFHC9OI.js} +0 -0
- /package/dist/web/assets/{mysql-BWq85KY4.js → mysql-qFvltsqN.js} +0 -0
- /package/dist/web/assets/{objective-c-D653JUMG.js → objective-c-Bnmr858J.js} +0 -0
- /package/dist/web/assets/{pascal-rWjRDdnR.js → pascal-WP0_D5AO.js} +0 -0
- /package/dist/web/assets/{pascaligo-Db8EehaF.js → pascaligo-Blom4Rij.js} +0 -0
- /package/dist/web/assets/{perl-C68oq8-D.js → perl-B-vk8g64.js} +0 -0
- /package/dist/web/assets/{pgsql-BXeHe33s.js → pgsql-Cgvz6v67.js} +0 -0
- /package/dist/web/assets/{php-CDVsAbfl.js → php-8a3Lrw9m.js} +0 -0
- /package/dist/web/assets/{pla-DnryFT0q.js → pla-DuFqEZ8V.js} +0 -0
- /package/dist/web/assets/{postiats-CDg_4Ev-.js → postiats-DkLtSgkp.js} +0 -0
- /package/dist/web/assets/{powerquery-CWPi8ROz.js → powerquery-BJ1aNepW.js} +0 -0
- /package/dist/web/assets/{powershell-C5A0QX3-.js → powershell-rE98k687.js} +0 -0
- /package/dist/web/assets/{preload-helper-DSXbuxSR.js → preload-helper-DWTEM3RW.js} +0 -0
- /package/dist/web/assets/{protobuf-Cgt-BQbL.js → protobuf-CUheFacr.js} +0 -0
- /package/dist/web/assets/{pug-RPYJC9QB.js → pug-LDcAMD8w.js} +0 -0
- /package/dist/web/assets/{qsharp-BZ3S7fu_.js → qsharp-IHfqKOfK.js} +0 -0
- /package/dist/web/assets/{r-CN875f1X.js → r-D-QApv87.js} +0 -0
- /package/dist/web/assets/{redis-BLesvTwR.js → redis-SXdDyWR9.js} +0 -0
- /package/dist/web/assets/{redshift-Byf_0XqD.js → redshift-Y6lsCryn.js} +0 -0
- /package/dist/web/assets/{restructuredtext-DYg_6BiZ.js → restructuredtext-edObr9a8.js} +0 -0
- /package/dist/web/assets/{ruby-C4OkxbC-.js → ruby-CNnUfF-8.js} +0 -0
- /package/dist/web/assets/{rust-xAoaEFMh.js → rust-IHUZWzBr.js} +0 -0
- /package/dist/web/assets/{sb-C8dHOW_y.js → sb-DrUvY44N.js} +0 -0
- /package/dist/web/assets/{scala-Spx0wP1o.js → scala-B4hbXGLM.js} +0 -0
- /package/dist/web/assets/{scheme-D2mZlAUz.js → scheme-BGrd12j3.js} +0 -0
- /package/dist/web/assets/{scss-DDCn3Ylu.js → scss-x5G1ES4U.js} +0 -0
- /package/dist/web/assets/{shell-M6px0EWn.js → shell-DOehe2Y8.js} +0 -0
- /package/dist/web/assets/{solidity-DUWMJi-f.js → solidity-BeRvcwWV.js} +0 -0
- /package/dist/web/assets/{sophia-DwJbUG-2.js → sophia-DZbkUNjy.js} +0 -0
- /package/dist/web/assets/{sparql-ClQxbRPI.js → sparql-B7_oi5-h.js} +0 -0
- /package/dist/web/assets/{sql-BQdjW7Vy.js → sql-CTlsFWVE.js} +0 -0
- /package/dist/web/assets/{st-BpISyZ_v.js → st-DJVEJdPE.js} +0 -0
- /package/dist/web/assets/{swift-CMbl5gM4.js → swift-CwhT3fYa.js} +0 -0
- /package/dist/web/assets/{systemverilog-jx2Xs7uO.js → systemverilog-BQN63pkN.js} +0 -0
- /package/dist/web/assets/{tcl-GIGnfs89.js → tcl-DqwfpskA.js} +0 -0
- /package/dist/web/assets/{twig-Bc0mxc_m.js → twig-BiyenUgc.js} +0 -0
- /package/dist/web/assets/{typespec-CEioAsEm.js → typespec-CWOJribt.js} +0 -0
- /package/dist/web/assets/{vb-BPk67J-d.js → vb-Cq5F87m3.js} +0 -0
- /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
|
+
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-SWEG-HYl.mjs";
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import process from "node:process";
|
|
@@ -6,14 +6,14 @@ import { fileURLToPath } from "node:url";
|
|
|
6
6
|
import { ZodError } from "zod";
|
|
7
7
|
import fs from "node:fs/promises";
|
|
8
8
|
import { defineCommand, runMain } from "citty";
|
|
9
|
+
import { consola } from "consola";
|
|
9
10
|
import updateNotifier from "update-notifier";
|
|
10
11
|
import CliTable3 from "cli-table3";
|
|
11
|
-
import { consola } from "consola";
|
|
12
12
|
import { intro, outro, spinner } from "@clack/prompts";
|
|
13
13
|
import Database from "better-sqlite3";
|
|
14
14
|
import pc from "picocolors";
|
|
15
15
|
import { createOpenAICompatible } from "@ai-sdk/openai-compatible";
|
|
16
|
-
import { Output, generateText, jsonSchema } from "ai";
|
|
16
|
+
import { APICallError, Output, generateText, jsonSchema } from "ai";
|
|
17
17
|
import { exec, execFile } from "node:child_process";
|
|
18
18
|
import { promisify } from "node:util";
|
|
19
19
|
import { serve } from "@hono/node-server";
|
|
@@ -95,6 +95,64 @@ function parseAllSchemas(entries) {
|
|
|
95
95
|
};
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
+
//#endregion
|
|
99
|
+
//#region src/commands/completion.ts
|
|
100
|
+
function bashScript(name$1) {
|
|
101
|
+
return `# ${name$1} bash completion
|
|
102
|
+
_${name$1}() {
|
|
103
|
+
local IFS=\\$'\\n'
|
|
104
|
+
COMPREPLY=($(${name$1} _complete "\${COMP_WORDS[@]}" 2>/dev/null))
|
|
105
|
+
}
|
|
106
|
+
complete -F _${name$1} ${name$1}
|
|
107
|
+
`;
|
|
108
|
+
}
|
|
109
|
+
function zshScript(name$1) {
|
|
110
|
+
return `# ${name$1} zsh completion
|
|
111
|
+
#compdef ${name$1}
|
|
112
|
+
|
|
113
|
+
_${name$1}() {
|
|
114
|
+
local -a completions
|
|
115
|
+
completions=("\${(@f)$(${name$1} _complete "\${words[@]}" 2>/dev/null)}")
|
|
116
|
+
_describe '${name$1}' completions
|
|
117
|
+
}
|
|
118
|
+
compdef _${name$1} ${name$1}
|
|
119
|
+
`;
|
|
120
|
+
}
|
|
121
|
+
function fishScript(name$1) {
|
|
122
|
+
return `# ${name$1} fish completion
|
|
123
|
+
complete -c ${name$1} -f -a '(${name$1} _complete (commandline -cp) 2>/dev/null)'
|
|
124
|
+
`;
|
|
125
|
+
}
|
|
126
|
+
function generateScript(name$1, shell) {
|
|
127
|
+
switch (shell) {
|
|
128
|
+
case "bash": return bashScript(name$1);
|
|
129
|
+
case "zsh": return zshScript(name$1);
|
|
130
|
+
case "fish": return fishScript(name$1);
|
|
131
|
+
default: throw new Error(`Unsupported shell: ${shell}. Use bash, zsh, or fish.`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
const completionCommand = defineCommand({
|
|
135
|
+
meta: {
|
|
136
|
+
name: "completion",
|
|
137
|
+
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"
|
|
138
|
+
},
|
|
139
|
+
args: { shell: {
|
|
140
|
+
type: "string",
|
|
141
|
+
description: "Shell type: bash, zsh, fish",
|
|
142
|
+
required: true
|
|
143
|
+
} },
|
|
144
|
+
async run({ args }) {
|
|
145
|
+
const name$1 = "aiex";
|
|
146
|
+
const shell = args.shell;
|
|
147
|
+
try {
|
|
148
|
+
process.stdout.write(generateScript(name$1, shell));
|
|
149
|
+
} catch (error) {
|
|
150
|
+
process.stderr.write(`Error: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
151
|
+
process.exit(1);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
|
|
98
156
|
//#endregion
|
|
99
157
|
//#region src/commands/doctor.ts
|
|
100
158
|
const doctorCommand = defineCommand({
|
|
@@ -12703,246 +12761,26 @@ function lookupModelCapabilities(modelName) {
|
|
|
12703
12761
|
}
|
|
12704
12762
|
|
|
12705
12763
|
//#endregion
|
|
12706
|
-
//#region
|
|
12707
|
-
|
|
12708
|
-
|
|
12709
|
-
|
|
12710
|
-
|
|
12711
|
-
|
|
12712
|
-
|
|
12713
|
-
|
|
12714
|
-
|
|
12715
|
-
|
|
12716
|
-
|
|
12717
|
-
|
|
12718
|
-
|
|
12719
|
-
|
|
12720
|
-
|
|
12721
|
-
|
|
12722
|
-
|
|
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
|
-
}
|
|
12764
|
+
//#region src/utils/retry.ts
|
|
12765
|
+
async function withRetry(fn, onRetry, maxRetries = 5) {
|
|
12766
|
+
let lastError;
|
|
12767
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) try {
|
|
12768
|
+
return await fn();
|
|
12769
|
+
} catch (error) {
|
|
12770
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
12771
|
+
lastError = err;
|
|
12772
|
+
if (!(err instanceof APICallError && err.isRetryable && attempt < maxRetries)) throw err;
|
|
12773
|
+
const delayMs = 1e3 * 2 ** attempt + Math.round(Math.random() * 500);
|
|
12774
|
+
onRetry?.({
|
|
12775
|
+
attempt: attempt + 1,
|
|
12776
|
+
maxRetries,
|
|
12777
|
+
delayMs,
|
|
12778
|
+
statusCode: err.statusCode
|
|
12779
|
+
});
|
|
12780
|
+
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
12944
12781
|
}
|
|
12945
|
-
|
|
12782
|
+
throw lastError ?? /* @__PURE__ */ new Error("Retry failed after all attempts");
|
|
12783
|
+
}
|
|
12946
12784
|
|
|
12947
12785
|
//#endregion
|
|
12948
12786
|
//#region src/core/ai-extraction/json-utils.ts
|
|
@@ -13132,6 +12970,100 @@ async function readFilePart(filePath) {
|
|
|
13132
12970
|
mimeType: mime
|
|
13133
12971
|
};
|
|
13134
12972
|
}
|
|
12973
|
+
function nullableType(type) {
|
|
12974
|
+
return type === "null" ? ["null"] : [type, "null"];
|
|
12975
|
+
}
|
|
12976
|
+
function propertyToExtractionSchema(property) {
|
|
12977
|
+
if (property.type === "array") return {
|
|
12978
|
+
type: nullableType("array"),
|
|
12979
|
+
items: property.items ? propertyToExtractionSchema(property.items) : {}
|
|
12980
|
+
};
|
|
12981
|
+
if (property.type === "object") {
|
|
12982
|
+
const childProperties = property.properties ? Object.fromEntries(Object.entries(property.properties).map(([name$1, prop]) => [name$1, propertyToExtractionSchema(prop)])) : void 0;
|
|
12983
|
+
return {
|
|
12984
|
+
type: nullableType("object"),
|
|
12985
|
+
...childProperties ? {
|
|
12986
|
+
properties: childProperties,
|
|
12987
|
+
required: Object.keys(childProperties),
|
|
12988
|
+
additionalProperties: false
|
|
12989
|
+
} : { additionalProperties: true }
|
|
12990
|
+
};
|
|
12991
|
+
}
|
|
12992
|
+
return { type: nullableType(property.type) };
|
|
12993
|
+
}
|
|
12994
|
+
function isRecord(value) {
|
|
12995
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
12996
|
+
}
|
|
12997
|
+
function schemaToExtractionOutputSchema(schema) {
|
|
12998
|
+
const properties = Object.fromEntries(Object.entries(schema.properties).filter(([, prop]) => !(prop.primary && prop.autoIncrement)).map(([name$1, prop]) => [name$1, propertyToExtractionSchema(prop)]));
|
|
12999
|
+
return {
|
|
13000
|
+
type: "object",
|
|
13001
|
+
additionalProperties: false,
|
|
13002
|
+
properties,
|
|
13003
|
+
required: Object.keys(properties)
|
|
13004
|
+
};
|
|
13005
|
+
}
|
|
13006
|
+
function validatePropertyValue(path$1, property, value, issues) {
|
|
13007
|
+
if (value === null) return;
|
|
13008
|
+
switch (property.type) {
|
|
13009
|
+
case "string":
|
|
13010
|
+
if (typeof value !== "string") issues.push(`${path$1}: expected string or null`);
|
|
13011
|
+
return;
|
|
13012
|
+
case "integer":
|
|
13013
|
+
if (!Number.isInteger(value)) issues.push(`${path$1}: expected integer or null`);
|
|
13014
|
+
return;
|
|
13015
|
+
case "number":
|
|
13016
|
+
if (typeof value !== "number" || Number.isNaN(value)) issues.push(`${path$1}: expected number or null`);
|
|
13017
|
+
return;
|
|
13018
|
+
case "boolean":
|
|
13019
|
+
if (typeof value !== "boolean") issues.push(`${path$1}: expected boolean or null`);
|
|
13020
|
+
return;
|
|
13021
|
+
case "array":
|
|
13022
|
+
if (!Array.isArray(value)) {
|
|
13023
|
+
issues.push(`${path$1}: expected array or null`);
|
|
13024
|
+
return;
|
|
13025
|
+
}
|
|
13026
|
+
if (property.items) {
|
|
13027
|
+
const itemProperty = property.items;
|
|
13028
|
+
value.forEach((item, index) => validatePropertyValue(`${path$1}[${index}]`, itemProperty, item, issues));
|
|
13029
|
+
}
|
|
13030
|
+
return;
|
|
13031
|
+
case "object":
|
|
13032
|
+
if (!isRecord(value)) {
|
|
13033
|
+
issues.push(`${path$1}: expected object or null`);
|
|
13034
|
+
return;
|
|
13035
|
+
}
|
|
13036
|
+
if (property.properties) validateProperties(path$1, property.properties, value, issues);
|
|
13037
|
+
return;
|
|
13038
|
+
case "null": issues.push(`${path$1}: expected null`);
|
|
13039
|
+
}
|
|
13040
|
+
}
|
|
13041
|
+
function validateProperties(basePath, properties, data, issues) {
|
|
13042
|
+
const expected = Object.entries(properties).filter(([, prop]) => !(prop.primary && prop.autoIncrement));
|
|
13043
|
+
const expectedKeys = new Set(expected.map(([name$1]) => name$1));
|
|
13044
|
+
for (const key of Object.keys(data)) if (!expectedKeys.has(key)) issues.push(`${basePath}.${key}: unexpected field`);
|
|
13045
|
+
for (const [name$1, prop] of expected) {
|
|
13046
|
+
const path$1 = `${basePath}.${name$1}`;
|
|
13047
|
+
if (!(name$1 in data)) {
|
|
13048
|
+
issues.push(`${path$1}: missing field`);
|
|
13049
|
+
continue;
|
|
13050
|
+
}
|
|
13051
|
+
validatePropertyValue(path$1, prop, data[name$1], issues);
|
|
13052
|
+
}
|
|
13053
|
+
}
|
|
13054
|
+
function validateExtractedData(schema, data) {
|
|
13055
|
+
if (!isRecord(data)) return {
|
|
13056
|
+
success: false,
|
|
13057
|
+
error: "Extracted data must be a JSON object."
|
|
13058
|
+
};
|
|
13059
|
+
const issues = [];
|
|
13060
|
+
validateProperties("$", schema.properties, data, issues);
|
|
13061
|
+
if (issues.length > 0) return {
|
|
13062
|
+
success: false,
|
|
13063
|
+
error: `Extracted data does not match schema:\n${issues.map((issue) => ` - ${issue}`).join("\n")}`
|
|
13064
|
+
};
|
|
13065
|
+
return { success: true };
|
|
13066
|
+
}
|
|
13135
13067
|
async function loadPromptSnapshot(aiexDir, tableName) {
|
|
13136
13068
|
const snapshotPath = path.join(aiexDir, "extracted", `${tableName}.prompt.md`);
|
|
13137
13069
|
try {
|
|
@@ -13173,15 +13105,16 @@ async function extractStructuredData(input) {
|
|
|
13173
13105
|
let system;
|
|
13174
13106
|
let user;
|
|
13175
13107
|
const snapshot = await loadPromptSnapshot(aiexDir, schema.table.name);
|
|
13108
|
+
const promptText = file ? PLACEHOLDER_TEXT : text;
|
|
13176
13109
|
if (snapshot) {
|
|
13177
13110
|
system = snapshot.system;
|
|
13178
|
-
user = snapshot.user.replaceAll(PLACEHOLDER_TEXT,
|
|
13111
|
+
user = snapshot.user.replaceAll(PLACEHOLDER_TEXT, promptText);
|
|
13179
13112
|
} else {
|
|
13180
|
-
const generated = generateExtractionPrompt(schema,
|
|
13113
|
+
const generated = generateExtractionPrompt(schema, promptText, config.prompt ?? DEFAULT_PROMPT_CONFIG);
|
|
13181
13114
|
system = generated.system;
|
|
13182
13115
|
user = generated.user;
|
|
13183
13116
|
}
|
|
13184
|
-
const outputSchema = jsonSchema(
|
|
13117
|
+
const outputSchema = jsonSchema(schemaToExtractionOutputSchema(schema));
|
|
13185
13118
|
let result;
|
|
13186
13119
|
if (useFileContent) {
|
|
13187
13120
|
const filePart = await readFilePart(file);
|
|
@@ -13208,23 +13141,30 @@ async function extractStructuredData(input) {
|
|
|
13208
13141
|
role: "user",
|
|
13209
13142
|
content: contentParts
|
|
13210
13143
|
}],
|
|
13211
|
-
abortSignal: AbortSignal.timeout(12e4)
|
|
13144
|
+
abortSignal: AbortSignal.timeout(12e4),
|
|
13145
|
+
maxRetries: 0
|
|
13212
13146
|
};
|
|
13213
13147
|
if (useStructuredOutput) fileOpts.output = Output.object({ schema: outputSchema });
|
|
13214
|
-
result = await generateText(fileOpts);
|
|
13148
|
+
result = await withRetry(() => generateText(fileOpts), input.onRetry);
|
|
13215
13149
|
} else {
|
|
13216
13150
|
const textOpts = {
|
|
13217
13151
|
model: provider.chatModel(selected.name),
|
|
13218
13152
|
system,
|
|
13219
13153
|
prompt: user,
|
|
13220
|
-
abortSignal: AbortSignal.timeout(6e4)
|
|
13154
|
+
abortSignal: AbortSignal.timeout(6e4),
|
|
13155
|
+
maxRetries: 0
|
|
13221
13156
|
};
|
|
13222
13157
|
if (useStructuredOutput) textOpts.output = Output.object({ schema: outputSchema });
|
|
13223
|
-
result = await generateText(textOpts);
|
|
13158
|
+
result = await withRetry(() => generateText(textOpts), input.onRetry);
|
|
13224
13159
|
}
|
|
13225
13160
|
let data;
|
|
13226
13161
|
if (useStructuredOutput) data = result.output;
|
|
13227
13162
|
else data = safeParseJSON(result.text);
|
|
13163
|
+
const validation = validateExtractedData(schema, data);
|
|
13164
|
+
if (!validation.success) return {
|
|
13165
|
+
success: false,
|
|
13166
|
+
error: validation.error
|
|
13167
|
+
};
|
|
13228
13168
|
const outputDir = path.resolve(aiexDir, config.extraction.outputDir.replace(".aiex/", ""));
|
|
13229
13169
|
await fs.mkdir(outputDir, { recursive: true });
|
|
13230
13170
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
@@ -13409,6 +13349,12 @@ const IMAGE_EXTENSIONS = new Set([
|
|
|
13409
13349
|
"bmp",
|
|
13410
13350
|
"svg"
|
|
13411
13351
|
]);
|
|
13352
|
+
const FILE_PART_EXTENSIONS = new Set([...IMAGE_EXTENSIONS, "pdf"]);
|
|
13353
|
+
function fail$1(message) {
|
|
13354
|
+
if (message) consola.error(message);
|
|
13355
|
+
outro("Failed!");
|
|
13356
|
+
process.exitCode = 1;
|
|
13357
|
+
}
|
|
13412
13358
|
async function ensureDatabaseReady(dbPath, schema) {
|
|
13413
13359
|
try {
|
|
13414
13360
|
await fs.access(dbPath);
|
|
@@ -13467,29 +13413,24 @@ const extractCommand = defineCommand({
|
|
|
13467
13413
|
const config = createMigrationConfig(process.cwd());
|
|
13468
13414
|
const aiexDir = path.dirname(config.schemaPath);
|
|
13469
13415
|
if (!args.text && !args.file) {
|
|
13470
|
-
|
|
13471
|
-
outro("Failed!");
|
|
13416
|
+
fail$1("Please provide text (-t) or a file (-f) to extract from");
|
|
13472
13417
|
return;
|
|
13473
13418
|
}
|
|
13474
13419
|
if (args.text && args.file) {
|
|
13475
|
-
|
|
13476
|
-
outro("Failed!");
|
|
13420
|
+
fail$1("-t and -f cannot be used together");
|
|
13477
13421
|
return;
|
|
13478
13422
|
}
|
|
13479
13423
|
const aiConfig = await readAIConfig(aiexDir);
|
|
13480
13424
|
if (!aiConfig) {
|
|
13481
|
-
|
|
13482
|
-
outro("Failed!");
|
|
13425
|
+
fail$1("AI configuration not found. Please configure AI settings in the Web interface first");
|
|
13483
13426
|
return;
|
|
13484
13427
|
}
|
|
13485
13428
|
if (!aiConfig.provider.apiKey) {
|
|
13486
|
-
|
|
13487
|
-
outro("Failed!");
|
|
13429
|
+
fail$1("API Key not configured. Please configure AI settings in the Web interface first");
|
|
13488
13430
|
return;
|
|
13489
13431
|
}
|
|
13490
13432
|
if (!aiConfig.provider.models?.length) {
|
|
13491
|
-
|
|
13492
|
-
outro("Failed!");
|
|
13433
|
+
fail$1("No models configured. Please add at least one model in AI Settings");
|
|
13493
13434
|
return;
|
|
13494
13435
|
}
|
|
13495
13436
|
let modelOverride;
|
|
@@ -13497,8 +13438,7 @@ const extractCommand = defineCommand({
|
|
|
13497
13438
|
const matched = aiConfig.provider.models.find((m) => m.name === args.model);
|
|
13498
13439
|
if (!matched) {
|
|
13499
13440
|
const available = aiConfig.provider.models.map((m) => m.name).join(", ");
|
|
13500
|
-
|
|
13501
|
-
outro("Failed!");
|
|
13441
|
+
fail$1(`Model "${args.model}" not found in configuration. Available models: ${available}`);
|
|
13502
13442
|
return;
|
|
13503
13443
|
}
|
|
13504
13444
|
modelOverride = matched;
|
|
@@ -13507,12 +13447,11 @@ const extractCommand = defineCommand({
|
|
|
13507
13447
|
let filePath;
|
|
13508
13448
|
if (args.file) {
|
|
13509
13449
|
const ext = path.extname(args.file).toLowerCase().replace(".", "");
|
|
13510
|
-
if (
|
|
13450
|
+
if (FILE_PART_EXTENSIONS.has(ext)) filePath = args.file;
|
|
13511
13451
|
else try {
|
|
13512
13452
|
text = await fs.readFile(args.file, "utf-8");
|
|
13513
13453
|
} catch {
|
|
13514
|
-
|
|
13515
|
-
outro("Failed!");
|
|
13454
|
+
fail$1(`Cannot read file: ${args.file}`);
|
|
13516
13455
|
return;
|
|
13517
13456
|
}
|
|
13518
13457
|
} else if (args.text) text = args.text;
|
|
@@ -13523,18 +13462,17 @@ const extractCommand = defineCommand({
|
|
|
13523
13462
|
const content = await fs.readFile(schemaPath, "utf-8");
|
|
13524
13463
|
schema = JSON.parse(content);
|
|
13525
13464
|
} catch {
|
|
13526
|
-
|
|
13527
|
-
outro("Failed!");
|
|
13465
|
+
fail$1(`Cannot read schema file: ${schemaName}.json`);
|
|
13528
13466
|
return;
|
|
13529
13467
|
}
|
|
13530
13468
|
try {
|
|
13531
|
-
JsonSchemaDefinitionSchema.parse(schema);
|
|
13469
|
+
schema = JsonSchemaDefinitionSchema.parse(schema);
|
|
13532
13470
|
} catch (e) {
|
|
13533
13471
|
if (e instanceof ZodError) {
|
|
13534
13472
|
consola.error(`Schema validation failed: ${schemaName}.json`);
|
|
13535
13473
|
for (const issue of e.issues) consola.error(` - ${issue.path.join(".")}: ${issue.message}`);
|
|
13536
13474
|
}
|
|
13537
|
-
|
|
13475
|
+
fail$1();
|
|
13538
13476
|
return;
|
|
13539
13477
|
}
|
|
13540
13478
|
const s = spinner();
|
|
@@ -13545,12 +13483,14 @@ const extractCommand = defineCommand({
|
|
|
13545
13483
|
text,
|
|
13546
13484
|
aiexDir,
|
|
13547
13485
|
file: filePath,
|
|
13548
|
-
modelOverride
|
|
13486
|
+
modelOverride,
|
|
13487
|
+
onRetry(info) {
|
|
13488
|
+
s.message(`API responded with ${info.statusCode}, retrying in ${info.delayMs / 1e3}s (${info.attempt}/${info.maxRetries})...`);
|
|
13489
|
+
}
|
|
13549
13490
|
});
|
|
13550
13491
|
if (!result.success) {
|
|
13551
13492
|
s.stop("Extraction failed");
|
|
13552
|
-
|
|
13553
|
-
outro("Failed!");
|
|
13493
|
+
fail$1(result.error || "Unknown error");
|
|
13554
13494
|
return;
|
|
13555
13495
|
}
|
|
13556
13496
|
s.stop("Extraction complete");
|
|
@@ -13562,8 +13502,7 @@ const extractCommand = defineCommand({
|
|
|
13562
13502
|
const dbError = await ensureDatabaseReady(config.databasePath, schema);
|
|
13563
13503
|
if (dbError) {
|
|
13564
13504
|
s2.stop("Database not ready");
|
|
13565
|
-
|
|
13566
|
-
outro("Failed!");
|
|
13505
|
+
fail$1(dbError);
|
|
13567
13506
|
return;
|
|
13568
13507
|
}
|
|
13569
13508
|
try {
|
|
@@ -13573,8 +13512,7 @@ const extractCommand = defineCommand({
|
|
|
13573
13512
|
if (insertResult.success) s2.stop(`Inserted into ${insertResult.tablesInserted.length} table(s)`);
|
|
13574
13513
|
else {
|
|
13575
13514
|
s2.stop("Database insert failed");
|
|
13576
|
-
|
|
13577
|
-
outro("Failed!");
|
|
13515
|
+
fail$1(insertResult.error || "Unknown error");
|
|
13578
13516
|
return;
|
|
13579
13517
|
}
|
|
13580
13518
|
} finally {
|
|
@@ -13582,8 +13520,7 @@ const extractCommand = defineCommand({
|
|
|
13582
13520
|
}
|
|
13583
13521
|
} catch (e) {
|
|
13584
13522
|
s2.stop("Database insert failed");
|
|
13585
|
-
|
|
13586
|
-
outro("Failed!");
|
|
13523
|
+
fail$1(e instanceof Error ? e.message : String(e));
|
|
13587
13524
|
return;
|
|
13588
13525
|
}
|
|
13589
13526
|
}
|
|
@@ -13591,9 +13528,27 @@ const extractCommand = defineCommand({
|
|
|
13591
13528
|
}
|
|
13592
13529
|
});
|
|
13593
13530
|
|
|
13531
|
+
//#endregion
|
|
13532
|
+
//#region schemas/table-schema.json
|
|
13533
|
+
var $id = "https://raw.githubusercontent.com/OSpoon/aiex-cli/main/app/cli/schemas/table-schema.json";
|
|
13534
|
+
|
|
13594
13535
|
//#endregion
|
|
13595
13536
|
//#region src/commands/schema.ts
|
|
13596
13537
|
const execFileAsync$1 = promisify(execFile);
|
|
13538
|
+
function fail(message) {
|
|
13539
|
+
if (message) consola.error(message);
|
|
13540
|
+
outro("Failed!");
|
|
13541
|
+
process.exitCode = 1;
|
|
13542
|
+
}
|
|
13543
|
+
async function writeJsonIfAbsent(filePath, data) {
|
|
13544
|
+
try {
|
|
13545
|
+
await fs.writeFile(filePath, `${JSON.stringify(data, null, 2)}\n`, { flag: "wx" });
|
|
13546
|
+
return "created";
|
|
13547
|
+
} catch (error) {
|
|
13548
|
+
if (error.code === "EEXIST") return "skipped";
|
|
13549
|
+
throw error;
|
|
13550
|
+
}
|
|
13551
|
+
}
|
|
13597
13552
|
async function generateFromFiles(schemaFiles, config) {
|
|
13598
13553
|
const result = parseAllSchemas(await Promise.all(schemaFiles.map(async (filePath) => {
|
|
13599
13554
|
return {
|
|
@@ -13611,17 +13566,18 @@ async function generateFromFiles(schemaFiles, config) {
|
|
|
13611
13566
|
consola.success(`Generated ${pc.cyan(".aiex/drizzle/schema.ts")} from ${schemaFiles.length} schema file(s)`);
|
|
13612
13567
|
return true;
|
|
13613
13568
|
}
|
|
13614
|
-
async function migrate(config) {
|
|
13569
|
+
async function migrate(config, migrationName) {
|
|
13615
13570
|
const helperPath = resolveHelperPath();
|
|
13616
|
-
const
|
|
13571
|
+
const helperArgs = [
|
|
13572
|
+
resolveTsxPath(),
|
|
13573
|
+
helperPath,
|
|
13574
|
+
config.drizzleSchemaPath,
|
|
13575
|
+
config.migrationsPath,
|
|
13576
|
+
config.databasePath
|
|
13577
|
+
];
|
|
13578
|
+
if (migrationName) helperArgs.push(migrationName);
|
|
13617
13579
|
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() });
|
|
13580
|
+
const { stdout } = await execFileAsync$1(process.execPath, helperArgs, { cwd: process.cwd() });
|
|
13625
13581
|
const result = JSON.parse(stdout.trim());
|
|
13626
13582
|
if (!result.success) {
|
|
13627
13583
|
consola.error("Failed to generate migration");
|
|
@@ -13862,9 +13818,11 @@ const schemaCommand = defineCommand({
|
|
|
13862
13818
|
"authorId"
|
|
13863
13819
|
]
|
|
13864
13820
|
};
|
|
13865
|
-
await
|
|
13866
|
-
await
|
|
13821
|
+
const userStatus = await writeJsonIfAbsent(path.join(config.schemaPath, "user.json"), userSchema);
|
|
13822
|
+
const postStatus = await writeJsonIfAbsent(path.join(config.schemaPath, "post.json"), postSchema);
|
|
13867
13823
|
consola.success(`Initialized ${pc.cyan(".aiex/")} with example schemas`);
|
|
13824
|
+
if (userStatus === "skipped") consola.warn(`${pc.cyan(".aiex/schema/user.json")} already exists, skipped`);
|
|
13825
|
+
if (postStatus === "skipped") consola.warn(`${pc.cyan(".aiex/schema/post.json")} already exists, skipped`);
|
|
13868
13826
|
consola.info("Example includes: User (with preferences has-one), Post (with comments has-many)");
|
|
13869
13827
|
outro("Run: aiex schema");
|
|
13870
13828
|
return;
|
|
@@ -13877,9 +13835,8 @@ const schemaCommand = defineCommand({
|
|
|
13877
13835
|
schemaFiles = [];
|
|
13878
13836
|
}
|
|
13879
13837
|
if (schemaFiles.length === 0) {
|
|
13880
|
-
consola.error(`No schema files found in ${pc.cyan(".aiex/schema/")}`);
|
|
13881
13838
|
consola.info("Use --init to initialize with an example schema");
|
|
13882
|
-
|
|
13839
|
+
fail(`No schema files found in ${pc.cyan(".aiex/schema/")}`);
|
|
13883
13840
|
return;
|
|
13884
13841
|
}
|
|
13885
13842
|
const s1 = spinner();
|
|
@@ -13887,7 +13844,7 @@ const schemaCommand = defineCommand({
|
|
|
13887
13844
|
const genOk = await generateFromFiles(schemaFiles, config);
|
|
13888
13845
|
s1.stop(genOk ? "Schema generated" : "Generation failed");
|
|
13889
13846
|
if (!genOk) {
|
|
13890
|
-
|
|
13847
|
+
fail();
|
|
13891
13848
|
return;
|
|
13892
13849
|
}
|
|
13893
13850
|
if (args.generate) {
|
|
@@ -13896,10 +13853,10 @@ const schemaCommand = defineCommand({
|
|
|
13896
13853
|
}
|
|
13897
13854
|
const s2 = spinner();
|
|
13898
13855
|
s2.start("Running migrations...");
|
|
13899
|
-
const migOk = await migrate(config);
|
|
13856
|
+
const migOk = await migrate(config, args.name);
|
|
13900
13857
|
s2.stop(migOk ? "Migrations applied" : "Migration failed");
|
|
13901
13858
|
if (!migOk) {
|
|
13902
|
-
|
|
13859
|
+
fail();
|
|
13903
13860
|
return;
|
|
13904
13861
|
}
|
|
13905
13862
|
outro("Done!");
|
|
@@ -13977,6 +13934,8 @@ function aiRoutes(config) {
|
|
|
13977
13934
|
//#endregion
|
|
13978
13935
|
//#region src/server/routes/data.ts
|
|
13979
13936
|
const FILE_REGEX = /\.json$/;
|
|
13937
|
+
const EXTRACTION_FILE_RE = /^[\w.-]+\.json$/;
|
|
13938
|
+
const TABLE_NAME_RE$1 = /^[a-z][a-z0-9_]*$/;
|
|
13980
13939
|
const TIMESTAMP_CLEANUP = /(\d{2})-(\d{2})-(\d{2})/;
|
|
13981
13940
|
const TIMESTAMP_TZ = /(\d{3})Z/;
|
|
13982
13941
|
function dataRoutes(config) {
|
|
@@ -14014,20 +13973,6 @@ function dataRoutes(config) {
|
|
|
14014
13973
|
return c.json({ error: error instanceof Error ? error.message : String(error) }, 500);
|
|
14015
13974
|
}
|
|
14016
13975
|
});
|
|
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
13976
|
app.get("/data/tables", async (c) => {
|
|
14032
13977
|
try {
|
|
14033
13978
|
const schemaDir = config.schemaPath;
|
|
@@ -14066,6 +14011,7 @@ function dataRoutes(config) {
|
|
|
14066
14011
|
});
|
|
14067
14012
|
app.get("/data/tables/:name", async (c) => {
|
|
14068
14013
|
const tableName = c.req.param("name");
|
|
14014
|
+
if (!TABLE_NAME_RE$1.test(tableName)) return c.json({ error: "Invalid table name" }, 400);
|
|
14069
14015
|
const sortField = c.req.query("sortField");
|
|
14070
14016
|
const sortOrder = c.req.query("sortOrder") || "asc";
|
|
14071
14017
|
let db;
|
|
@@ -14100,12 +14046,33 @@ function dataRoutes(config) {
|
|
|
14100
14046
|
db.close();
|
|
14101
14047
|
}
|
|
14102
14048
|
});
|
|
14049
|
+
app.get("/data/:name", async (c) => {
|
|
14050
|
+
const name$1 = c.req.param("name");
|
|
14051
|
+
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);
|
|
14052
|
+
const filePath = path.join(extractedDir, name$1);
|
|
14053
|
+
try {
|
|
14054
|
+
const content = await fs.readFile(filePath, "utf-8");
|
|
14055
|
+
return c.json({
|
|
14056
|
+
success: true,
|
|
14057
|
+
content,
|
|
14058
|
+
name: name$1
|
|
14059
|
+
});
|
|
14060
|
+
} catch {
|
|
14061
|
+
return c.json({ error: "Extraction result not found" }, 404);
|
|
14062
|
+
}
|
|
14063
|
+
});
|
|
14103
14064
|
return app;
|
|
14104
14065
|
}
|
|
14105
14066
|
|
|
14106
14067
|
//#endregion
|
|
14107
14068
|
//#region src/server/routes/schema.ts
|
|
14108
14069
|
const execFileAsync = promisify(execFile);
|
|
14070
|
+
const SCHEMA_FILE_RE = /^[\w.-]+\.json$/;
|
|
14071
|
+
const TABLE_NAME_RE = /^[a-z][a-z0-9_]*$/;
|
|
14072
|
+
function resolveSchemaFile(schemaDir, name$1) {
|
|
14073
|
+
if (name$1 !== path.basename(name$1) || !SCHEMA_FILE_RE.test(name$1) || name$1.includes("..")) return null;
|
|
14074
|
+
return path.join(schemaDir, name$1);
|
|
14075
|
+
}
|
|
14109
14076
|
function schemaRoutes(config) {
|
|
14110
14077
|
const app = new Hono();
|
|
14111
14078
|
const schemaDir = config.schemaPath;
|
|
@@ -14118,8 +14085,8 @@ function schemaRoutes(config) {
|
|
|
14118
14085
|
return c.json(jsonFiles);
|
|
14119
14086
|
});
|
|
14120
14087
|
app.get("/schema/:name", async (c) => {
|
|
14121
|
-
const
|
|
14122
|
-
|
|
14088
|
+
const filePath = resolveSchemaFile(schemaDir, c.req.param("name"));
|
|
14089
|
+
if (!filePath) return c.json({ error: "Invalid schema file name" }, 400);
|
|
14123
14090
|
try {
|
|
14124
14091
|
const content = await fs.readFile(filePath, "utf-8");
|
|
14125
14092
|
return c.json(JSON.parse(content));
|
|
@@ -14128,8 +14095,8 @@ function schemaRoutes(config) {
|
|
|
14128
14095
|
}
|
|
14129
14096
|
});
|
|
14130
14097
|
app.post("/schema/:name", async (c) => {
|
|
14131
|
-
const
|
|
14132
|
-
|
|
14098
|
+
const filePath = resolveSchemaFile(schemaDir, c.req.param("name"));
|
|
14099
|
+
if (!filePath) return c.json({ error: "Invalid schema file name" }, 400);
|
|
14133
14100
|
try {
|
|
14134
14101
|
const body = await c.req.json();
|
|
14135
14102
|
await ensureDir();
|
|
@@ -14145,6 +14112,10 @@ function schemaRoutes(config) {
|
|
|
14145
14112
|
});
|
|
14146
14113
|
app.get("/prompt-snapshot/:name", async (c) => {
|
|
14147
14114
|
const name$1 = c.req.param("name");
|
|
14115
|
+
if (!TABLE_NAME_RE.test(name$1)) return c.json({
|
|
14116
|
+
success: false,
|
|
14117
|
+
error: "Invalid table name"
|
|
14118
|
+
}, 400);
|
|
14148
14119
|
const aiexDir = path.dirname(schemaDir);
|
|
14149
14120
|
const snapshotPath = path.join(aiexDir, "extracted", `${name$1}.prompt.md`);
|
|
14150
14121
|
try {
|
|
@@ -14161,8 +14132,8 @@ function schemaRoutes(config) {
|
|
|
14161
14132
|
}
|
|
14162
14133
|
});
|
|
14163
14134
|
app.delete("/schema/:name", async (c) => {
|
|
14164
|
-
const
|
|
14165
|
-
|
|
14135
|
+
const filePath = resolveSchemaFile(schemaDir, c.req.param("name"));
|
|
14136
|
+
if (!filePath) return c.json({ error: "Invalid schema file name" }, 400);
|
|
14166
14137
|
try {
|
|
14167
14138
|
await fs.unlink(filePath);
|
|
14168
14139
|
return c.json({ success: true });
|
|
@@ -14239,9 +14210,10 @@ function schemaRoutes(config) {
|
|
|
14239
14210
|
|
|
14240
14211
|
//#endregion
|
|
14241
14212
|
//#region src/server/index.ts
|
|
14213
|
+
const LOCAL_ORIGIN_RE = /^https?:\/\/(?:localhost|127\.0\.0\.1|\[::1\])(?::\d+)?$/;
|
|
14242
14214
|
function createApp(config, staticDir) {
|
|
14243
14215
|
const app = new Hono();
|
|
14244
|
-
app.use("*", cors());
|
|
14216
|
+
app.use("*", cors({ origin: (origin) => LOCAL_ORIGIN_RE.test(origin) ? origin : null }));
|
|
14245
14217
|
app.route("/api", schemaRoutes(config));
|
|
14246
14218
|
app.route("/api", aiRoutes(config));
|
|
14247
14219
|
app.route("/api", dataRoutes(config));
|
|
@@ -14317,6 +14289,7 @@ const subCommands = {
|
|
|
14317
14289
|
web: webCommand,
|
|
14318
14290
|
schema: schemaCommand,
|
|
14319
14291
|
extract: extractCommand,
|
|
14292
|
+
completion: completionCommand,
|
|
14320
14293
|
doctor: doctorCommand
|
|
14321
14294
|
};
|
|
14322
14295
|
|
|
@@ -14324,11 +14297,26 @@ const subCommands = {
|
|
|
14324
14297
|
//#region src/cli.ts
|
|
14325
14298
|
seedConfig(createConfig());
|
|
14326
14299
|
updateNotifier({ pkg: package_default }).notify();
|
|
14300
|
+
process.on("uncaughtException", (error) => {
|
|
14301
|
+
consola.error(error);
|
|
14302
|
+
process.exit(1);
|
|
14303
|
+
});
|
|
14304
|
+
process.on("unhandledRejection", (reason) => {
|
|
14305
|
+
const error = reason instanceof Error ? reason : new Error(String(reason));
|
|
14306
|
+
consola.error(error);
|
|
14307
|
+
process.exit(1);
|
|
14308
|
+
});
|
|
14309
|
+
if (process.argv[2] === "_complete") {
|
|
14310
|
+
const { getCompletions } = await import("./completions-ygS1okck.mjs");
|
|
14311
|
+
const suggestions = getCompletions(subCommands, process.argv.slice(3));
|
|
14312
|
+
for (const s of suggestions) process.stdout.write(`${s}\n`);
|
|
14313
|
+
process.exit(0);
|
|
14314
|
+
}
|
|
14327
14315
|
runMain(defineCommand({
|
|
14328
14316
|
meta: {
|
|
14329
14317
|
name,
|
|
14330
14318
|
version,
|
|
14331
|
-
description
|
|
14319
|
+
description
|
|
14332
14320
|
},
|
|
14333
14321
|
subCommands
|
|
14334
14322
|
}));
|