blokctl 0.6.15 → 0.6.17
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/dist/commands/create/project.js +2 -2
- package/dist/commands/gen/appTypes.d.ts +13 -0
- package/dist/commands/gen/appTypes.js +195 -0
- package/dist/commands/gen/appTypes.test.d.ts +1 -0
- package/dist/commands/gen/appTypes.test.js +116 -0
- package/dist/commands/gen/index.d.ts +1 -0
- package/dist/commands/gen/index.js +14 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/package.json +2 -2
|
@@ -17,7 +17,7 @@ const exec = util.promisify(child_process.exec);
|
|
|
17
17
|
const HOME_DIR = `${os.homedir()}/.blok`;
|
|
18
18
|
const GITHUB_REPO_LOCAL = `${HOME_DIR}/blok`;
|
|
19
19
|
const GITHUB_REPO_REMOTE = "https://github.com/well-prado/blok.git";
|
|
20
|
-
const GITHUB_REPO_RELEASE_TAG = "v0.6.
|
|
20
|
+
const GITHUB_REPO_RELEASE_TAG = "v0.6.17";
|
|
21
21
|
fsExtra.ensureDirSync(HOME_DIR);
|
|
22
22
|
const options = {
|
|
23
23
|
baseDir: HOME_DIR,
|
|
@@ -487,7 +487,7 @@ export async function createProject(opts, version, currentPath = false, localRep
|
|
|
487
487
|
"@blokjs/trigger-websocket": "triggers/websocket",
|
|
488
488
|
"@blokjs/trigger-worker": "triggers/worker",
|
|
489
489
|
};
|
|
490
|
-
const BLOKJS_DEP_RANGE = "^0.6.
|
|
490
|
+
const BLOKJS_DEP_RANGE = "^0.6.17";
|
|
491
491
|
for (const depGroup of ["dependencies", "devDependencies", "peerDependencies"]) {
|
|
492
492
|
const deps = packageJsonContent[depGroup];
|
|
493
493
|
if (!deps)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { OptionValues } from "commander";
|
|
2
|
+
export interface WorkflowEntry {
|
|
3
|
+
name: string;
|
|
4
|
+
file: string;
|
|
5
|
+
}
|
|
6
|
+
export declare function extractWorkflowName(source: string): string | null;
|
|
7
|
+
export declare function nameToIdentifier(name: string): string;
|
|
8
|
+
export declare function importSpecifier(outFile: string, workflowFile: string): string;
|
|
9
|
+
export declare function buildAppTypeSource(entries: readonly WorkflowEntry[], outFile: string): {
|
|
10
|
+
source: string;
|
|
11
|
+
collisions: string[];
|
|
12
|
+
};
|
|
13
|
+
export declare function generateAppTypes(opts: OptionValues): Promise<void>;
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import { promises as fsp } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import color from "picocolors";
|
|
4
|
+
export function extractWorkflowName(source) {
|
|
5
|
+
const stripped = source.replace(/\/\*[\s\S]*?\*\//g, "").replace(/\/\/[^\n]*/g, "");
|
|
6
|
+
const factory = /\b(?:workflow|Workflow)\s*\(/.exec(stripped);
|
|
7
|
+
if (!factory)
|
|
8
|
+
return null;
|
|
9
|
+
const rest = stripped.slice(factory.index);
|
|
10
|
+
const nameMatch = /\bname\s*:\s*(["'`])([^"'`]+)\1/.exec(rest);
|
|
11
|
+
return nameMatch ? nameMatch[2].trim() : null;
|
|
12
|
+
}
|
|
13
|
+
export function nameToIdentifier(name) {
|
|
14
|
+
const parts = name.split(/[^A-Za-z0-9]+/).filter(Boolean);
|
|
15
|
+
const camel = parts.map((p, i) => (i === 0 ? p : p.charAt(0).toUpperCase() + p.slice(1))).join("");
|
|
16
|
+
const safe = camel.length === 0 ? "wf" : camel;
|
|
17
|
+
return /^[0-9]/.test(safe) ? `wf_${safe}` : safe;
|
|
18
|
+
}
|
|
19
|
+
export function importSpecifier(outFile, workflowFile) {
|
|
20
|
+
let rel = path.relative(path.dirname(outFile), workflowFile).replace(/\\/g, "/").replace(/\.ts$/, "");
|
|
21
|
+
if (!rel.startsWith("."))
|
|
22
|
+
rel = `./${rel}`;
|
|
23
|
+
return rel;
|
|
24
|
+
}
|
|
25
|
+
function isLeaf(node) {
|
|
26
|
+
return typeof node.__leaf === "string";
|
|
27
|
+
}
|
|
28
|
+
export function buildAppTypeSource(entries, outFile) {
|
|
29
|
+
const sorted = [...entries].sort((a, b) => a.name.localeCompare(b.name));
|
|
30
|
+
const collisions = [];
|
|
31
|
+
const usedIdents = new Map();
|
|
32
|
+
const imports = [];
|
|
33
|
+
const tree = {};
|
|
34
|
+
for (const entry of sorted) {
|
|
35
|
+
let ident = nameToIdentifier(entry.name);
|
|
36
|
+
if (usedIdents.has(ident) && usedIdents.get(ident) !== entry.name) {
|
|
37
|
+
let n = 2;
|
|
38
|
+
while (usedIdents.has(`${ident}${n}`))
|
|
39
|
+
n++;
|
|
40
|
+
ident = `${ident}${n}`;
|
|
41
|
+
}
|
|
42
|
+
usedIdents.set(ident, entry.name);
|
|
43
|
+
imports.push(`import type ${ident} from "${importSpecifier(outFile, entry.file)}";`);
|
|
44
|
+
const segments = entry.name.split(".").filter(Boolean);
|
|
45
|
+
let node = tree;
|
|
46
|
+
let collided = false;
|
|
47
|
+
for (let i = 0; i < segments.length; i++) {
|
|
48
|
+
const seg = segments[i];
|
|
49
|
+
const last = i === segments.length - 1;
|
|
50
|
+
if (last) {
|
|
51
|
+
if (node[seg] !== undefined) {
|
|
52
|
+
collisions.push(entry.name);
|
|
53
|
+
collided = true;
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
56
|
+
node[seg] = { __leaf: ident };
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
const existing = node[seg];
|
|
60
|
+
if (existing === undefined) {
|
|
61
|
+
const child = {};
|
|
62
|
+
node[seg] = child;
|
|
63
|
+
node = child;
|
|
64
|
+
}
|
|
65
|
+
else if (isLeaf(existing)) {
|
|
66
|
+
collisions.push(entry.name);
|
|
67
|
+
collided = true;
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
node = existing;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
if (collided)
|
|
76
|
+
imports.pop();
|
|
77
|
+
}
|
|
78
|
+
const render = (node, indent) => {
|
|
79
|
+
const lines = ["{"];
|
|
80
|
+
for (const key of Object.keys(node).sort()) {
|
|
81
|
+
const child = node[key];
|
|
82
|
+
const k = /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key) ? key : JSON.stringify(key);
|
|
83
|
+
if (isLeaf(child)) {
|
|
84
|
+
lines.push(`${indent}\t${k}: ${child.__leaf};`);
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
lines.push(`${indent}\t${k}: ${render(child, `${indent}\t`)};`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
lines.push(`${indent}}`);
|
|
91
|
+
return lines.join("\n");
|
|
92
|
+
};
|
|
93
|
+
const header = [
|
|
94
|
+
"// AUTO-GENERATED by `blokctl gen app-types`. Do not edit by hand.",
|
|
95
|
+
"// Regenerate after adding, removing, or renaming a workflow.",
|
|
96
|
+
"// Consumed by `@blokjs/client`: `createBlokClient<BlokApp>()`.",
|
|
97
|
+
"",
|
|
98
|
+
].join("\n");
|
|
99
|
+
const body = imports.length === 0
|
|
100
|
+
? "export type BlokApp = Record<string, never>;\n"
|
|
101
|
+
: `${imports.join("\n")}\n\nexport type BlokApp = ${render(tree, "")};\n`;
|
|
102
|
+
return { source: `${header}${body}`, collisions };
|
|
103
|
+
}
|
|
104
|
+
async function collectTsFiles(dir) {
|
|
105
|
+
const out = [];
|
|
106
|
+
let dirents;
|
|
107
|
+
try {
|
|
108
|
+
dirents = await fsp.readdir(dir, { withFileTypes: true });
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
return out;
|
|
112
|
+
}
|
|
113
|
+
for (const d of dirents) {
|
|
114
|
+
if (d.name.startsWith("_") || d.name.startsWith("."))
|
|
115
|
+
continue;
|
|
116
|
+
const full = path.join(dir, d.name);
|
|
117
|
+
if (d.isDirectory()) {
|
|
118
|
+
out.push(...(await collectTsFiles(full)));
|
|
119
|
+
}
|
|
120
|
+
else if (d.name.endsWith(".ts") &&
|
|
121
|
+
!d.name.endsWith(".test.ts") &&
|
|
122
|
+
!d.name.endsWith(".spec.ts") &&
|
|
123
|
+
!d.name.endsWith(".d.ts")) {
|
|
124
|
+
out.push(full);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return out;
|
|
128
|
+
}
|
|
129
|
+
async function resolveWorkflowsDir(cwd, explicit) {
|
|
130
|
+
const candidates = explicit
|
|
131
|
+
? [explicit]
|
|
132
|
+
: ["triggers/http/src/workflows", "src/workflows", "workflows/ts", "workflows"];
|
|
133
|
+
for (const c of candidates) {
|
|
134
|
+
const abs = path.isAbsolute(c) ? c : path.join(cwd, c);
|
|
135
|
+
try {
|
|
136
|
+
const stat = await fsp.stat(abs);
|
|
137
|
+
if (stat.isDirectory())
|
|
138
|
+
return abs;
|
|
139
|
+
}
|
|
140
|
+
catch {
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
export async function generateAppTypes(opts) {
|
|
146
|
+
const cwd = process.cwd();
|
|
147
|
+
const explicitDir = opts.dir ?? null;
|
|
148
|
+
console.log(color.cyan("\n🧬 Blok app-types generator"));
|
|
149
|
+
console.log(color.dim("Generates the typed `BlokApp` index consumed by @blokjs/client.\n"));
|
|
150
|
+
const dir = await resolveWorkflowsDir(cwd, explicitDir);
|
|
151
|
+
if (!dir) {
|
|
152
|
+
console.log(color.red("❌ Could not find a TS workflows directory. Looked in: triggers/http/src/workflows/, " +
|
|
153
|
+
"src/workflows/, workflows/. Pass --dir <path> to override."));
|
|
154
|
+
process.exit(1);
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
const outFile = path.isAbsolute(opts.out ?? "")
|
|
158
|
+
? opts.out
|
|
159
|
+
: path.join(cwd, opts.out ?? "blok-app.d.ts");
|
|
160
|
+
console.log(color.dim(`Scanning ${color.cyan(dir)} (recursive)\n`));
|
|
161
|
+
const files = await collectTsFiles(dir);
|
|
162
|
+
const entries = [];
|
|
163
|
+
const skipped = [];
|
|
164
|
+
for (const file of files) {
|
|
165
|
+
const src = await fsp.readFile(file, "utf8");
|
|
166
|
+
const name = extractWorkflowName(src);
|
|
167
|
+
if (name)
|
|
168
|
+
entries.push({ name, file });
|
|
169
|
+
else
|
|
170
|
+
skipped.push(path.relative(cwd, file));
|
|
171
|
+
}
|
|
172
|
+
if (entries.length === 0) {
|
|
173
|
+
console.log(color.yellow("No TS workflows with a literal `name:` found — nothing to generate."));
|
|
174
|
+
if (skipped.length > 0)
|
|
175
|
+
console.log(color.dim(`Skipped (no literal name): ${skipped.join(", ")}`));
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
const { source, collisions } = buildAppTypeSource(entries, outFile);
|
|
179
|
+
if (opts.dryRun === true) {
|
|
180
|
+
console.log(color.dim(`— dry run — would write ${color.cyan(path.relative(cwd, outFile))}:\n`));
|
|
181
|
+
console.log(source);
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
await fsp.mkdir(path.dirname(outFile), { recursive: true });
|
|
185
|
+
await fsp.writeFile(outFile, source, "utf8");
|
|
186
|
+
console.log(color.green(`✅ Wrote ${color.cyan(path.relative(cwd, outFile))} (${entries.length} workflow(s)).`));
|
|
187
|
+
}
|
|
188
|
+
for (const c of collisions) {
|
|
189
|
+
console.log(color.yellow(`⚠️ name collision — "${c}" overlaps another workflow's path and was dropped.`));
|
|
190
|
+
}
|
|
191
|
+
if (skipped.length > 0) {
|
|
192
|
+
console.log(color.dim(`ℹ️ Skipped ${skipped.length} file(s) without a literal workflow name (dynamic name or not a workflow): ${skipped.join(", ")}`));
|
|
193
|
+
}
|
|
194
|
+
console.log(color.dim('\nNext: `import type { BlokApp } from "<out>"` and `createBlokClient<BlokApp>({ baseUrl })`.\n'));
|
|
195
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { promises as fsp } from "node:fs";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { afterEach, describe, expect, it } from "vitest";
|
|
5
|
+
import { buildAppTypeSource, extractWorkflowName, generateAppTypes, importSpecifier, nameToIdentifier, } from "./appTypes.js";
|
|
6
|
+
describe("extractWorkflowName", () => {
|
|
7
|
+
it("extracts the name from a v2 workflow() factory call", () => {
|
|
8
|
+
const src = `import { workflow } from "@blokjs/helper";
|
|
9
|
+
export default workflow({ name: "users.list", version: "1.0.0", trigger: { http: { method: "GET" } }, steps: [{ id: "s", use: "@blokjs/respond", inputs: {} }] });`;
|
|
10
|
+
expect(extractWorkflowName(src)).toBe("users.list");
|
|
11
|
+
});
|
|
12
|
+
it("handles the legacy Workflow() factory and single quotes", () => {
|
|
13
|
+
expect(extractWorkflowName("const x = Workflow({ name: 'orders.create' })")).toBe("orders.create");
|
|
14
|
+
});
|
|
15
|
+
it("anchors on the factory call, not a step/node `name:`", () => {
|
|
16
|
+
const src = `export default workflow({
|
|
17
|
+
name: "the.workflow",
|
|
18
|
+
steps: [{ id: "x", use: "n", inputs: { name: "not-the-workflow" } }],
|
|
19
|
+
});`;
|
|
20
|
+
expect(extractWorkflowName(src)).toBe("the.workflow");
|
|
21
|
+
});
|
|
22
|
+
it("ignores a commented-out name", () => {
|
|
23
|
+
const src = `// name: "commented"
|
|
24
|
+
/* name: "blocked" */
|
|
25
|
+
export default workflow({ name: "real.name" });`;
|
|
26
|
+
expect(extractWorkflowName(src)).toBe("real.name");
|
|
27
|
+
});
|
|
28
|
+
it("returns null when there is no workflow factory call", () => {
|
|
29
|
+
expect(extractWorkflowName(`export const x = { name: "not.a.workflow" };`)).toBeNull();
|
|
30
|
+
});
|
|
31
|
+
it("returns null when the name is a non-literal (variable)", () => {
|
|
32
|
+
expect(extractWorkflowName(`export default workflow({ name: WF_NAME, version: "1" })`)).toBeNull();
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
describe("nameToIdentifier", () => {
|
|
36
|
+
it("camelCases a dotted name", () => {
|
|
37
|
+
expect(nameToIdentifier("users.list")).toBe("usersList");
|
|
38
|
+
expect(nameToIdentifier("a.b.c")).toBe("aBC");
|
|
39
|
+
});
|
|
40
|
+
it("passes a single segment through", () => {
|
|
41
|
+
expect(nameToIdentifier("health")).toBe("health");
|
|
42
|
+
});
|
|
43
|
+
it("prefixes identifiers that would start with a digit", () => {
|
|
44
|
+
expect(nameToIdentifier("2fa.verify")).toBe("wf_2faVerify");
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
describe("importSpecifier", () => {
|
|
48
|
+
it("computes a relative, extensionless, ./-prefixed specifier", () => {
|
|
49
|
+
const spec = importSpecifier("/proj/blok-app.d.ts", "/proj/triggers/http/src/workflows/users/list.ts");
|
|
50
|
+
expect(spec).toBe("./triggers/http/src/workflows/users/list");
|
|
51
|
+
});
|
|
52
|
+
it("emits ../ when the workflow is above the output dir", () => {
|
|
53
|
+
const spec = importSpecifier("/proj/web/blok-app.d.ts", "/proj/server/workflows/health.ts");
|
|
54
|
+
expect(spec).toBe("../server/workflows/health");
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
describe("buildAppTypeSource", () => {
|
|
58
|
+
const out = "/proj/blok-app.d.ts";
|
|
59
|
+
it("nests workflows by their dotted name and imports each by file", () => {
|
|
60
|
+
const entries = [
|
|
61
|
+
{ name: "users.list", file: "/proj/wf/users/list.ts" },
|
|
62
|
+
{ name: "users.create", file: "/proj/wf/users/create.ts" },
|
|
63
|
+
{ name: "health", file: "/proj/wf/health.ts" },
|
|
64
|
+
];
|
|
65
|
+
const { source, collisions } = buildAppTypeSource(entries, out);
|
|
66
|
+
expect(collisions).toEqual([]);
|
|
67
|
+
expect(source).toContain('import type usersList from "./wf/users/list";');
|
|
68
|
+
expect(source).toContain('import type usersCreate from "./wf/users/create";');
|
|
69
|
+
expect(source).toContain('import type health from "./wf/health";');
|
|
70
|
+
expect(source).toContain("export type BlokApp = {");
|
|
71
|
+
expect(source).toContain("users: {");
|
|
72
|
+
expect(source).toContain("list: usersList;");
|
|
73
|
+
expect(source).toContain("create: usersCreate;");
|
|
74
|
+
expect(source).toContain("health: health;");
|
|
75
|
+
});
|
|
76
|
+
it("flags a leaf-vs-group collision and drops the loser", () => {
|
|
77
|
+
const entries = [
|
|
78
|
+
{ name: "users", file: "/proj/wf/users.ts" },
|
|
79
|
+
{ name: "users.list", file: "/proj/wf/users/list.ts" },
|
|
80
|
+
];
|
|
81
|
+
const { collisions } = buildAppTypeSource(entries, out);
|
|
82
|
+
expect(collisions.length).toBe(1);
|
|
83
|
+
});
|
|
84
|
+
it("quotes non-identifier path segments", () => {
|
|
85
|
+
const { source } = buildAppTypeSource([{ name: "weird-name.go", file: "/proj/wf/x.ts" }], out);
|
|
86
|
+
expect(source).toContain('"weird-name":');
|
|
87
|
+
});
|
|
88
|
+
it("emits an empty-but-valid type when there are no workflows", () => {
|
|
89
|
+
const { source } = buildAppTypeSource([], out);
|
|
90
|
+
expect(source).toContain("export type BlokApp = Record<string, never>;");
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
describe("generateAppTypes (fs integration)", () => {
|
|
94
|
+
const tmpDirs = [];
|
|
95
|
+
afterEach(async () => {
|
|
96
|
+
for (const d of tmpDirs)
|
|
97
|
+
await fsp.rm(d, { recursive: true, force: true });
|
|
98
|
+
tmpDirs.length = 0;
|
|
99
|
+
});
|
|
100
|
+
it("scans a workflows dir and writes blok-app.d.ts", async () => {
|
|
101
|
+
const root = await fsp.mkdtemp(path.join(os.tmpdir(), "blok-gen-"));
|
|
102
|
+
tmpDirs.push(root);
|
|
103
|
+
const wfDir = path.join(root, "workflows", "users");
|
|
104
|
+
await fsp.mkdir(wfDir, { recursive: true });
|
|
105
|
+
await fsp.writeFile(path.join(wfDir, "list.ts"), `import { workflow } from "@blokjs/helper";
|
|
106
|
+
export default workflow({ name: "users.list", version: "1.0.0", trigger: { http: { method: "GET" } }, steps: [] });`);
|
|
107
|
+
await fsp.writeFile(path.join(root, "workflows", "_helper.ts"), "export const x = 1;");
|
|
108
|
+
const out = path.join(root, "blok-app.d.ts");
|
|
109
|
+
await generateAppTypes({ dir: path.join(root, "workflows"), out });
|
|
110
|
+
const written = await fsp.readFile(out, "utf8");
|
|
111
|
+
expect(written).toContain("export type BlokApp = {");
|
|
112
|
+
expect(written).toContain("users: {");
|
|
113
|
+
expect(written).toContain("list: usersList;");
|
|
114
|
+
expect(written).toContain('import type usersList from "./workflows/users/list";');
|
|
115
|
+
});
|
|
116
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { program } from "../../services/commander.js";
|
|
3
|
+
import { generateAppTypes } from "./appTypes.js";
|
|
4
|
+
const gen = new Command("gen").description("Generate typed client artifacts for @blokjs/client");
|
|
5
|
+
const appTypes = new Command("app-types")
|
|
6
|
+
.description("Generate the typed `BlokApp` index (blok-app.d.ts) from your TS workflow files")
|
|
7
|
+
.option("-d, --dir <value>", "TS workflows directory (defaults to triggers/http/src/workflows, src/workflows, or workflows)")
|
|
8
|
+
.option("-o, --out <value>", "Output file (default: ./blok-app.d.ts)")
|
|
9
|
+
.option("--dry-run", "Print the generated file without writing it")
|
|
10
|
+
.action(async (options) => {
|
|
11
|
+
await generateAppTypes(options);
|
|
12
|
+
});
|
|
13
|
+
gen.addCommand(appTypes);
|
|
14
|
+
program.addCommand(gen);
|
package/dist/index.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ import "./commands/publish/index.js";
|
|
|
8
8
|
import "./commands/install/index.js";
|
|
9
9
|
import "./commands/search/index.js";
|
|
10
10
|
import "./commands/generate/index.js";
|
|
11
|
+
import "./commands/gen/index.js";
|
|
11
12
|
import "./commands/config/index.js";
|
|
12
13
|
import "./commands/migrate/index.js";
|
|
13
14
|
import "./commands/graph/index.js";
|
package/dist/index.js
CHANGED
|
@@ -23,6 +23,7 @@ import "./commands/publish/index.js";
|
|
|
23
23
|
import "./commands/install/index.js";
|
|
24
24
|
import "./commands/search/index.js";
|
|
25
25
|
import "./commands/generate/index.js";
|
|
26
|
+
import "./commands/gen/index.js";
|
|
26
27
|
import "./commands/config/index.js";
|
|
27
28
|
import "./commands/migrate/index.js";
|
|
28
29
|
import "./commands/graph/index.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "blokctl",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.17",
|
|
4
4
|
"author": "Deskree Technologies Inc.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"description": "cli for blok",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"keywords": ["blokctl", "cli", "blok", "blok"],
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@ai-sdk/openai": "^1.3.22",
|
|
33
|
-
"@blokjs/runner": "^0.6.
|
|
33
|
+
"@blokjs/runner": "^0.6.17",
|
|
34
34
|
"@clack/prompts": "^1.0.0",
|
|
35
35
|
"ai": "^4.3.16",
|
|
36
36
|
"better-sqlite3": "^12.6.2",
|