aiex-cli 0.0.1-beta.6 → 0.0.1-beta.8
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
CHANGED
|
@@ -100,6 +100,26 @@ By default, aiex automatically selects a model based on your input type (vision-
|
|
|
100
100
|
| `aiex extract -s <name> -f <file> --db` | Extract and insert into SQLite database |
|
|
101
101
|
| `aiex extract -s <name> -f <file> -m <model>` | Extract with a specific AI model |
|
|
102
102
|
| `aiex doctor` | System and configuration diagnostics |
|
|
103
|
+
| `aiex completion bash\|zsh\|fish` | Generate shell completion scripts |
|
|
104
|
+
|
|
105
|
+
### Shell Completions
|
|
106
|
+
|
|
107
|
+
Enable tab completion for commands and options:
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
# bash
|
|
111
|
+
source <(aiex completion bash)
|
|
112
|
+
|
|
113
|
+
# zsh
|
|
114
|
+
source <(aiex completion zsh)
|
|
115
|
+
|
|
116
|
+
# fish
|
|
117
|
+
aiex completion fish | source
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
To make it permanent, add the `source` line to your shell config file (`~/.bashrc`, `~/.zshrc`, or `~/.config/fish/config.fish`).
|
|
121
|
+
|
|
122
|
+
> Completions are dynamically generated from the command definitions — no manual updates needed when commands or options change.
|
|
103
123
|
|
|
104
124
|
<br>
|
|
105
125
|
|
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, 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-
|
|
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-DxG7uaGR.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({
|
|
@@ -12702,6 +12760,28 @@ function lookupModelCapabilities(modelName) {
|
|
|
12702
12760
|
return null;
|
|
12703
12761
|
}
|
|
12704
12762
|
|
|
12763
|
+
//#endregion
|
|
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));
|
|
12781
|
+
}
|
|
12782
|
+
throw lastError ?? /* @__PURE__ */ new Error("Retry failed after all attempts");
|
|
12783
|
+
}
|
|
12784
|
+
|
|
12705
12785
|
//#endregion
|
|
12706
12786
|
//#region src/core/ai-extraction/json-utils.ts
|
|
12707
12787
|
function stripFences(text) {
|
|
@@ -12876,18 +12956,18 @@ function detectMimeType(filePath) {
|
|
|
12876
12956
|
}
|
|
12877
12957
|
async function readFilePart(filePath) {
|
|
12878
12958
|
const mime = detectMimeType(filePath);
|
|
12879
|
-
const
|
|
12959
|
+
const buffer = await fs.readFile(filePath);
|
|
12880
12960
|
const name$1 = path.basename(filePath);
|
|
12881
12961
|
if (mime.startsWith("image/")) return {
|
|
12882
12962
|
type: "image",
|
|
12883
|
-
|
|
12884
|
-
|
|
12963
|
+
image: buffer,
|
|
12964
|
+
mimeType: mime
|
|
12885
12965
|
};
|
|
12886
12966
|
return {
|
|
12887
12967
|
type: "file",
|
|
12888
|
-
|
|
12889
|
-
|
|
12890
|
-
|
|
12968
|
+
data: buffer,
|
|
12969
|
+
mediaType: mime,
|
|
12970
|
+
filename: name$1
|
|
12891
12971
|
};
|
|
12892
12972
|
}
|
|
12893
12973
|
function nullableType(type) {
|
|
@@ -13038,42 +13118,32 @@ async function extractStructuredData(input) {
|
|
|
13038
13118
|
let result;
|
|
13039
13119
|
if (useFileContent) {
|
|
13040
13120
|
const filePart = await readFilePart(file);
|
|
13041
|
-
const fileName = filePart.name;
|
|
13042
13121
|
const contentParts = [{
|
|
13043
13122
|
type: "text",
|
|
13044
|
-
text: user.includes(PLACEHOLDER_TEXT) ? user.replaceAll(PLACEHOLDER_TEXT, text || `Data is contained in the attached file: ${
|
|
13045
|
-
}];
|
|
13046
|
-
if (filePart.type === "image") contentParts.push({
|
|
13047
|
-
type: "image",
|
|
13048
|
-
image: filePart.image
|
|
13049
|
-
});
|
|
13050
|
-
else contentParts.push({
|
|
13051
|
-
type: "file",
|
|
13052
|
-
data: filePart.data,
|
|
13053
|
-
mimeType: filePart.mimeType
|
|
13054
|
-
});
|
|
13123
|
+
text: user.includes(PLACEHOLDER_TEXT) ? user.replaceAll(PLACEHOLDER_TEXT, text || `Data is contained in the attached file: ${filePart.filename || path.basename(file)}`) : user
|
|
13124
|
+
}, filePart];
|
|
13055
13125
|
const fileOpts = {
|
|
13056
13126
|
model: provider.chatModel(selected.name),
|
|
13127
|
+
system,
|
|
13057
13128
|
messages: [{
|
|
13058
|
-
role: "system",
|
|
13059
|
-
content: system
|
|
13060
|
-
}, {
|
|
13061
13129
|
role: "user",
|
|
13062
13130
|
content: contentParts
|
|
13063
13131
|
}],
|
|
13064
|
-
abortSignal: AbortSignal.timeout(12e4)
|
|
13132
|
+
abortSignal: AbortSignal.timeout(12e4),
|
|
13133
|
+
maxRetries: 0
|
|
13065
13134
|
};
|
|
13066
13135
|
if (useStructuredOutput) fileOpts.output = Output.object({ schema: outputSchema });
|
|
13067
|
-
result = await generateText(fileOpts);
|
|
13136
|
+
result = await withRetry(() => generateText(fileOpts), input.onRetry);
|
|
13068
13137
|
} else {
|
|
13069
13138
|
const textOpts = {
|
|
13070
13139
|
model: provider.chatModel(selected.name),
|
|
13071
13140
|
system,
|
|
13072
13141
|
prompt: user,
|
|
13073
|
-
abortSignal: AbortSignal.timeout(6e4)
|
|
13142
|
+
abortSignal: AbortSignal.timeout(6e4),
|
|
13143
|
+
maxRetries: 0
|
|
13074
13144
|
};
|
|
13075
13145
|
if (useStructuredOutput) textOpts.output = Output.object({ schema: outputSchema });
|
|
13076
|
-
result = await generateText(textOpts);
|
|
13146
|
+
result = await withRetry(() => generateText(textOpts), input.onRetry);
|
|
13077
13147
|
}
|
|
13078
13148
|
let data;
|
|
13079
13149
|
if (useStructuredOutput) data = result.output;
|
|
@@ -13401,7 +13471,10 @@ const extractCommand = defineCommand({
|
|
|
13401
13471
|
text,
|
|
13402
13472
|
aiexDir,
|
|
13403
13473
|
file: filePath,
|
|
13404
|
-
modelOverride
|
|
13474
|
+
modelOverride,
|
|
13475
|
+
onRetry(info) {
|
|
13476
|
+
s.message(`API responded with ${info.statusCode}, retrying in ${info.delayMs / 1e3}s (${info.attempt}/${info.maxRetries})...`);
|
|
13477
|
+
}
|
|
13405
13478
|
});
|
|
13406
13479
|
if (!result.success) {
|
|
13407
13480
|
s.stop("Extraction failed");
|
|
@@ -14050,6 +14123,16 @@ function schemaRoutes(config) {
|
|
|
14050
14123
|
const filePath = resolveSchemaFile(schemaDir, c.req.param("name"));
|
|
14051
14124
|
if (!filePath) return c.json({ error: "Invalid schema file name" }, 400);
|
|
14052
14125
|
try {
|
|
14126
|
+
const aiexDir = path.dirname(schemaDir);
|
|
14127
|
+
try {
|
|
14128
|
+
const content = await fs.readFile(filePath, "utf-8");
|
|
14129
|
+
const parsed = JsonSchemaDefinitionSchema.safeParse(JSON.parse(content));
|
|
14130
|
+
if (parsed.success) {
|
|
14131
|
+
const tableName = parsed.data.table.name;
|
|
14132
|
+
const snapshotPath = path.join(aiexDir, "extracted", `${tableName}.prompt.md`);
|
|
14133
|
+
await fs.unlink(snapshotPath).catch(() => {});
|
|
14134
|
+
}
|
|
14135
|
+
} catch {}
|
|
14053
14136
|
await fs.unlink(filePath);
|
|
14054
14137
|
return c.json({ success: true });
|
|
14055
14138
|
} catch {
|
|
@@ -14204,6 +14287,7 @@ const subCommands = {
|
|
|
14204
14287
|
web: webCommand,
|
|
14205
14288
|
schema: schemaCommand,
|
|
14206
14289
|
extract: extractCommand,
|
|
14290
|
+
completion: completionCommand,
|
|
14207
14291
|
doctor: doctorCommand
|
|
14208
14292
|
};
|
|
14209
14293
|
|
|
@@ -14211,6 +14295,21 @@ const subCommands = {
|
|
|
14211
14295
|
//#region src/cli.ts
|
|
14212
14296
|
seedConfig(createConfig());
|
|
14213
14297
|
updateNotifier({ pkg: package_default }).notify();
|
|
14298
|
+
process.on("uncaughtException", (error) => {
|
|
14299
|
+
consola.error(error);
|
|
14300
|
+
process.exit(1);
|
|
14301
|
+
});
|
|
14302
|
+
process.on("unhandledRejection", (reason) => {
|
|
14303
|
+
const error = reason instanceof Error ? reason : new Error(String(reason));
|
|
14304
|
+
consola.error(error);
|
|
14305
|
+
process.exit(1);
|
|
14306
|
+
});
|
|
14307
|
+
if (process.argv[2] === "_complete") {
|
|
14308
|
+
const { getCompletions } = await import("./completions-ygS1okck.mjs");
|
|
14309
|
+
const suggestions = getCompletions(subCommands, process.argv.slice(3));
|
|
14310
|
+
for (const s of suggestions) process.stdout.write(`${s}\n`);
|
|
14311
|
+
process.exit(0);
|
|
14312
|
+
}
|
|
14214
14313
|
runMain(defineCommand({
|
|
14215
14314
|
meta: {
|
|
14216
14315
|
name,
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import process from "node:process";
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
|
|
5
|
+
//#region src/core/completions.ts
|
|
6
|
+
const LEADING_DASHES = /^-+/;
|
|
7
|
+
function getArgNames(cmd) {
|
|
8
|
+
if (!cmd.args) return [];
|
|
9
|
+
return Object.entries(cmd.args).flatMap(([key, arg]) => {
|
|
10
|
+
const names = [`--${key}`];
|
|
11
|
+
if (arg.alias) names.push(`-${arg.alias}`);
|
|
12
|
+
return names;
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
function getCommandNames(cmds) {
|
|
16
|
+
return Object.keys(cmds).filter((c) => !c.startsWith("_"));
|
|
17
|
+
}
|
|
18
|
+
function getFileCompletions(pattern) {
|
|
19
|
+
try {
|
|
20
|
+
const files = fs.readdirSync(path.dirname(pattern));
|
|
21
|
+
const ext = path.extname(pattern);
|
|
22
|
+
const prefix = path.basename(pattern).replace(ext, "");
|
|
23
|
+
return files.filter((f) => f.endsWith(ext) && f.startsWith(prefix)).map((f) => f.replace(ext, ""));
|
|
24
|
+
} catch {
|
|
25
|
+
return [];
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function getJsonModelNames(configPath) {
|
|
29
|
+
try {
|
|
30
|
+
const content = fs.readFileSync(configPath, "utf-8");
|
|
31
|
+
const config = JSON.parse(content);
|
|
32
|
+
if (config.provider?.models) return config.provider.models.map((m) => m.name);
|
|
33
|
+
} catch {}
|
|
34
|
+
return [];
|
|
35
|
+
}
|
|
36
|
+
function getValueCompletions(prevArg) {
|
|
37
|
+
switch (prevArg) {
|
|
38
|
+
case "--schema":
|
|
39
|
+
case "-s": {
|
|
40
|
+
const cwd = process.cwd();
|
|
41
|
+
return getFileCompletions(path.join(cwd, ".aiex/schema/*.json"));
|
|
42
|
+
}
|
|
43
|
+
case "--model":
|
|
44
|
+
case "-m": {
|
|
45
|
+
const cwd = process.cwd();
|
|
46
|
+
return getJsonModelNames(path.join(cwd, ".aiex/ai-config.json"));
|
|
47
|
+
}
|
|
48
|
+
case "--file":
|
|
49
|
+
case "-f":
|
|
50
|
+
case "--text":
|
|
51
|
+
case "-t":
|
|
52
|
+
case "--name":
|
|
53
|
+
case "--port":
|
|
54
|
+
case "-p": return [];
|
|
55
|
+
default: return [];
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
function getCompletions(subCommands, args) {
|
|
59
|
+
const cmds = getCommandNames(subCommands);
|
|
60
|
+
if (args.length <= 1) {
|
|
61
|
+
const word = args[0] ?? "";
|
|
62
|
+
if (!word) return cmds;
|
|
63
|
+
return cmds.filter((c) => c.startsWith(word));
|
|
64
|
+
}
|
|
65
|
+
const cmdName = args[0];
|
|
66
|
+
const cmd = subCommands[cmdName];
|
|
67
|
+
const rest = args.slice(1);
|
|
68
|
+
if (!cmd) {
|
|
69
|
+
const matched = cmds.filter((c) => c.startsWith(cmdName));
|
|
70
|
+
if (matched.length > 0) return matched;
|
|
71
|
+
return cmds;
|
|
72
|
+
}
|
|
73
|
+
const current = rest[rest.length - 1] ?? "";
|
|
74
|
+
const prev = rest.length > 1 ? rest[rest.length - 2] : void 0;
|
|
75
|
+
if (current.startsWith("-")) {
|
|
76
|
+
const names = getArgNames(cmd);
|
|
77
|
+
if (!current) return names;
|
|
78
|
+
const key = current.replace(LEADING_DASHES, "");
|
|
79
|
+
if (!key) return names;
|
|
80
|
+
return names.filter((n) => n.replace(LEADING_DASHES, "").startsWith(key));
|
|
81
|
+
}
|
|
82
|
+
if (!current && prev) {
|
|
83
|
+
const values = getValueCompletions(prev);
|
|
84
|
+
if (values.length > 0) return values;
|
|
85
|
+
}
|
|
86
|
+
return getArgNames(cmd);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
//#endregion
|
|
90
|
+
export { getCompletions };
|
|
@@ -411,7 +411,7 @@ function generateDrizzleConfig() {
|
|
|
411
411
|
//#endregion
|
|
412
412
|
//#region package.json
|
|
413
413
|
var name = "aiex-cli";
|
|
414
|
-
var version = "0.0.1-beta.
|
|
414
|
+
var version = "0.0.1-beta.8";
|
|
415
415
|
var description = "JSON Schema → SQLite with AI-powered data extraction";
|
|
416
416
|
var package_default = {
|
|
417
417
|
name,
|
|
@@ -452,13 +452,15 @@ var package_default = {
|
|
|
452
452
|
files: [
|
|
453
453
|
"bin",
|
|
454
454
|
"dist",
|
|
455
|
-
"src/core/schema-sqlite/migrate-helper.ts"
|
|
455
|
+
"src/core/schema-sqlite/migrate-helper.ts",
|
|
456
|
+
"src/core/schema-sqlite/migration-name.ts"
|
|
456
457
|
],
|
|
457
458
|
scripts: {
|
|
458
459
|
"build": "tsdown && pnpm --filter aiex-web build",
|
|
459
460
|
"dev": "tsdown --watch",
|
|
460
461
|
"start": "tsx src/index.ts",
|
|
461
462
|
"test": "vitest",
|
|
463
|
+
"coverage": "vitest --coverage",
|
|
462
464
|
"typecheck": "tsc",
|
|
463
465
|
"lint": "eslint .",
|
|
464
466
|
"prepublishOnly": "cp ../../README.md . && pnpm run build",
|
|
@@ -491,6 +493,7 @@ var package_default = {
|
|
|
491
493
|
"@types/better-sqlite3": "catalog:types",
|
|
492
494
|
"@types/node": "catalog:types",
|
|
493
495
|
"@types/update-notifier": "catalog:",
|
|
496
|
+
"@vitest/coverage-v8": "catalog:testing",
|
|
494
497
|
"eslint": "catalog:cli",
|
|
495
498
|
"publint": "catalog:cli",
|
|
496
499
|
"tsdown": "catalog:cli",
|
package/dist/index.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { C as doctorDiagnosticsTableRows, S as buildDoctorDiagnostics, _ as generateDrizzleConfig, g as createMigrationConfig, t as collectDoctorDiagnostics, v as JsonSchemaDefinitionSchema, w as formatDoctorDiagnosticsJson, x as generateDrizzleSchema, y as parseJsonSchema } from "./doctor-
|
|
1
|
+
import { C as doctorDiagnosticsTableRows, S as buildDoctorDiagnostics, _ as generateDrizzleConfig, g as createMigrationConfig, t as collectDoctorDiagnostics, v as JsonSchemaDefinitionSchema, w as formatDoctorDiagnosticsJson, x as generateDrizzleSchema, y as parseJsonSchema } from "./doctor-DxG7uaGR.mjs";
|
|
2
2
|
|
|
3
3
|
export { JsonSchemaDefinitionSchema, buildDoctorDiagnostics, collectDoctorDiagnostics, createMigrationConfig, doctorDiagnosticsTableRows, formatDoctorDiagnosticsJson, generateDrizzleConfig, generateDrizzleSchema, parseJsonSchema };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aiex-cli",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.0.1-beta.
|
|
4
|
+
"version": "0.0.1-beta.8",
|
|
5
5
|
"description": "JSON Schema → SQLite with AI-powered data extraction",
|
|
6
6
|
"author": "OSpoon <zxin088@gmail.com>",
|
|
7
7
|
"license": "MIT",
|
|
@@ -39,7 +39,8 @@
|
|
|
39
39
|
"files": [
|
|
40
40
|
"bin",
|
|
41
41
|
"dist",
|
|
42
|
-
"src/core/schema-sqlite/migrate-helper.ts"
|
|
42
|
+
"src/core/schema-sqlite/migrate-helper.ts",
|
|
43
|
+
"src/core/schema-sqlite/migration-name.ts"
|
|
43
44
|
],
|
|
44
45
|
"dependencies": {
|
|
45
46
|
"@ai-sdk/openai-compatible": "^2.0.47",
|
|
@@ -68,6 +69,7 @@
|
|
|
68
69
|
"@types/better-sqlite3": "^7.6.0",
|
|
69
70
|
"@types/node": "^25.6.0",
|
|
70
71
|
"@types/update-notifier": "^6.0.8",
|
|
72
|
+
"@vitest/coverage-v8": "^4.1.4",
|
|
71
73
|
"eslint": "^10.2.0",
|
|
72
74
|
"publint": "^0.3.18",
|
|
73
75
|
"tsdown": "^0.17.3",
|
|
@@ -80,6 +82,7 @@
|
|
|
80
82
|
"dev": "tsdown --watch",
|
|
81
83
|
"start": "tsx src/index.ts",
|
|
82
84
|
"test": "vitest",
|
|
85
|
+
"coverage": "vitest --coverage",
|
|
83
86
|
"typecheck": "tsc",
|
|
84
87
|
"lint": "eslint ."
|
|
85
88
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export function sanitizeMigrationName(name?: string): string | undefined {
|
|
2
|
+
if (!name)
|
|
3
|
+
return undefined
|
|
4
|
+
|
|
5
|
+
const slug = name
|
|
6
|
+
.trim()
|
|
7
|
+
.toLowerCase()
|
|
8
|
+
.replace(/[^a-z0-9_\s-]/g, '_')
|
|
9
|
+
.replace(/[\s-]+/g, '_')
|
|
10
|
+
.replace(/_+/g, '_')
|
|
11
|
+
.replace(/^_+|_+$/g, '')
|
|
12
|
+
|
|
13
|
+
return slug || undefined
|
|
14
|
+
}
|